API Review Feedback: Logging and DateTime updates (#1752)
This commit is contained in:
parent
f3e98ffb47
commit
622e8da4fe
@ -19,6 +19,7 @@ option(RUN_LONG_UNIT_TESTS "Tests that takes more than 5 minutes to complete. No
|
||||
option(BUILD_STORAGE_SAMPLES "Build sample application for Azure Storage clients" OFF)
|
||||
option(BUILD_PERFORMANCE_TESTS "Build the performance test library" OFF)
|
||||
option(MSVC_USE_STATIC_CRT "(MSVC only) Set to ON to link SDK with static CRT (/MT or /MTd switch)." OFF)
|
||||
option(BUILD_ENV_LOGGER "Build support for enabling logging to console when AZURE_LOG_LEVEL environment variable is set." ON)
|
||||
|
||||
include(AzureTransportAdapters)
|
||||
include(AzureVcpkg)
|
||||
|
||||
@ -22,6 +22,11 @@
|
||||
- Renamed `TelemetryPolicyOptions` to `TelemetryOptions`.
|
||||
- Renamed `ValuePolicyOptions` to `ValueOptions`.
|
||||
- Removed `StartTry()` from `Azure::Core::Http::Request`.
|
||||
- Changed type of `Azure::Core::Http::RetryOptions::StatusCodes` from `std::vector` to `std::set`.
|
||||
- Renamed `Azure::Core::Http::LoggingPolicy` to `LogPolicy`.
|
||||
- Introduced `Azure::Core::Http::LogOptions`, a mandatory parameter for `LogPolicy` construction. Entities that are not specified in the allow lists are hidden in the log.
|
||||
- Moved `Azure::Core::Logging` namespace entities to `Azure::Core::Logger` class.
|
||||
- Removed `Azure::Core::DateTime::GetRfc3339String()`: `Azure::Core::DateTime::ToString()` was extended to provide the same functionality.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
||||
@ -21,6 +21,10 @@ az_vcpkg_integrate()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if (NOT BUILD_ENV_LOGGER)
|
||||
add_compile_definitions(AZ_NO_ENV_LOGGER)
|
||||
endif()
|
||||
|
||||
if(BUILD_TRANSPORT_CURL)
|
||||
# min version for `CURLSSLOPT_NO_REVOKE`
|
||||
# https://curl.haxx.se/libcurl/c/CURLOPT_SSL_OPTIONS.html
|
||||
@ -59,15 +63,15 @@ set(
|
||||
inc/azure/core/internal/null_body_stream.hpp
|
||||
inc/azure/core/internal/strings.hpp
|
||||
inc/azure/core/io/body_stream.hpp
|
||||
inc/azure/core/logging/logging.hpp
|
||||
inc/azure/core/base64.hpp
|
||||
inc/azure/core/case_insensitive_map.hpp
|
||||
inc/azure/core/case_insensitive_containers.hpp
|
||||
inc/azure/core/context.hpp
|
||||
inc/azure/core/credentials.hpp
|
||||
inc/azure/core/datetime.hpp
|
||||
inc/azure/core/dll_import_export.hpp
|
||||
inc/azure/core/etag.hpp
|
||||
inc/azure/core/exception.hpp
|
||||
inc/azure/core/logger.hpp
|
||||
inc/azure/core/match_conditions.hpp
|
||||
inc/azure/core/modified_conditions.hpp
|
||||
inc/azure/core/nullable.hpp
|
||||
@ -87,7 +91,7 @@ set(
|
||||
src/cryptography/md5.cpp
|
||||
src/http/bearer_token_authentication_policy.cpp
|
||||
src/http/http.cpp
|
||||
src/http/logging_policy.cpp
|
||||
src/http/log_policy.cpp
|
||||
src/http/policy.cpp
|
||||
src/http/raw_response.cpp
|
||||
src/http/request.cpp
|
||||
@ -96,10 +100,12 @@ set(
|
||||
src/http/transport_policy.cpp
|
||||
src/http/url.cpp
|
||||
src/io/body_stream.cpp
|
||||
src/logging/logging.cpp
|
||||
src/base64.cpp
|
||||
src/context.cpp
|
||||
src/datetime.cpp
|
||||
src/environment_log_level_listener.cpp
|
||||
src/environment_log_level_listener_private.hpp
|
||||
src/logger.cpp
|
||||
src/operation_status.cpp
|
||||
src/strings.cpp
|
||||
src/version.cpp
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include "azure/core/dll_import_export.hpp"
|
||||
#include "azure/core/etag.hpp"
|
||||
#include "azure/core/exception.hpp"
|
||||
#include "azure/core/logger.hpp"
|
||||
#include "azure/core/match_conditions.hpp"
|
||||
#include "azure/core/modified_conditions.hpp"
|
||||
#include "azure/core/nullable.hpp"
|
||||
@ -38,6 +39,3 @@
|
||||
|
||||
// azure/core/io
|
||||
#include "azure/core/io/body_stream.hpp"
|
||||
|
||||
// azure/core/logging
|
||||
#include "azure/core/logging/logging.hpp"
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "azure/core/internal/strings.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Core {
|
||||
@ -22,4 +23,9 @@ namespace Azure { namespace Core {
|
||||
using CaseInsensitiveMap
|
||||
= std::map<std::string, std::string, Internal::Strings::CaseInsensitiveComparator>;
|
||||
|
||||
/**
|
||||
* @brief A type alias of `std::set<std::string>` with case-insensitive element comparison.
|
||||
*/
|
||||
using CaseInsensitiveSet = std::set<std::string, Internal::Strings::CaseInsensitiveComparator>;
|
||||
|
||||
}} // namespace Azure::Core
|
||||
@ -11,6 +11,7 @@
|
||||
#include "azure/core/dll_import_export.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Core {
|
||||
@ -33,8 +34,8 @@ namespace Azure { namespace Core {
|
||||
// It would not be possible to base this clock on steady_clock and provide an implementation
|
||||
// that universally works in any context in predictable manner. However, it does not mean that
|
||||
// implementation can't use steady_clock in conjunction with this clock: an author can get a
|
||||
// duration beteen two time_points of this clock (or between system_clock::time point ant this
|
||||
// clock's time_point), and add that duration to steady clock's time_point to get a new
|
||||
// duration between two time_points of this clock (or between system_clock::time point at
|
||||
// this clock's time_point), and add that duration to steady clock's time_point to get a new
|
||||
// time_point in the steady clock's "coordinate system".
|
||||
static constexpr bool is_steady = std::chrono::system_clock::is_steady;
|
||||
static time_point now() noexcept;
|
||||
@ -162,42 +163,18 @@ namespace Azure { namespace Core {
|
||||
*/
|
||||
static DateTime Parse(std::string const& dateTime, DateFormat format);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Get a string representation of the #Azure::Core::DateTime.
|
||||
*
|
||||
* @param format The representation format to use.
|
||||
* @param fractionFormat The format for the fraction part of the Datetime. Only supported by
|
||||
* RFC 3339.
|
||||
* @param fractionFormat The format for the fraction part of the DateTime. Only
|
||||
* supported by RFC3339.
|
||||
*
|
||||
* @throw std::invalid_argument If year exceeds 9999, or if \p format is not recognized.
|
||||
*/
|
||||
std::string ToString(DateFormat format, TimeFractionFormat fractionFormat) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get a string representation of the #Azure::Core::DateTime.
|
||||
*
|
||||
* @param format The representation format to use.
|
||||
*
|
||||
* @throw std::invalid_argument If year exceeds 9999, or if \p format is not recognized.
|
||||
*/
|
||||
std::string ToString(DateFormat format) const
|
||||
{
|
||||
return ToString(format, TimeFractionFormat::DropTrailingZeros);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get a string representation of the #Azure::Core::DateTime formatted with RFC 3339.
|
||||
*
|
||||
* @param fractionFormat The format that is applied to the fraction part from the RFC 3339 date.
|
||||
*
|
||||
* @throw std::invalid_argument If year exceeds 9999, or if \p format is not recognized.
|
||||
*/
|
||||
std::string GetRfc3339String(TimeFractionFormat fractionFormat) const
|
||||
{
|
||||
return ToString(DateFormat::Rfc3339, fractionFormat);
|
||||
};
|
||||
std::string ToString(
|
||||
DateFormat format,
|
||||
TimeFractionFormat fractionFormat = TimeFractionFormat::DropTrailingZeros) const;
|
||||
};
|
||||
|
||||
inline Details::Clock::time_point Details::Clock::now() noexcept
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "azure/core/case_insensitive_map.hpp"
|
||||
#include "azure/core/case_insensitive_containers.hpp"
|
||||
#include "azure/core/exception.hpp"
|
||||
#include "azure/core/internal/contract.hpp"
|
||||
#include "azure/core/io/body_stream.hpp"
|
||||
@ -52,6 +52,25 @@ namespace Azure { namespace Core { namespace Http {
|
||||
CaseInsensitiveMap& headers,
|
||||
std::string const& headerName,
|
||||
std::string const& headerValue);
|
||||
|
||||
inline std::string FormatEncodedUrlQueryParameters(
|
||||
std::map<std::string, std::string> const& encodedQueryParameters)
|
||||
{
|
||||
{
|
||||
std::string queryStr;
|
||||
if (!encodedQueryParameters.empty())
|
||||
{
|
||||
auto separ = '?';
|
||||
for (const auto& q : encodedQueryParameters)
|
||||
{
|
||||
queryStr += separ + q.first + '=' + q.second;
|
||||
separ = '&';
|
||||
}
|
||||
}
|
||||
|
||||
return queryStr;
|
||||
}
|
||||
}
|
||||
} // namespace Details
|
||||
|
||||
/********************* Exceptions **********************/
|
||||
@ -260,6 +279,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
// this set.
|
||||
const static std::unordered_set<unsigned char> defaultNonUrlEncodeChars;
|
||||
|
||||
std::string GetUrlWithoutQuery(bool relative) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Decodes \p value by transforming all escaped characters to it's non-encoded value.
|
||||
@ -424,6 +445,12 @@ namespace Azure { namespace Core { namespace Http {
|
||||
return m_encodedQueryParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Scheme, host, and path, without query parameters.
|
||||
* @return Absolute URL without query parameters.
|
||||
*/
|
||||
std::string GetUrlWithoutQuery() const { return GetUrlWithoutQuery(false); }
|
||||
|
||||
/**
|
||||
* @brief Get the path and query parameters.
|
||||
*
|
||||
|
||||
@ -8,9 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "azure/core/case_insensitive_map.hpp"
|
||||
#include "azure/core/case_insensitive_containers.hpp"
|
||||
#include "azure/core/context.hpp"
|
||||
#include "azure/core/credentials.hpp"
|
||||
#include "azure/core/dll_import_export.hpp"
|
||||
#include "azure/core/http/http.hpp"
|
||||
#include "azure/core/http/transport.hpp"
|
||||
#include "azure/core/uuid.hpp"
|
||||
@ -20,6 +21,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -102,12 +104,12 @@ namespace Azure { namespace Core { namespace Http {
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct an abstraction representing a next line in the stack sequence of policies,
|
||||
* from the caller's perspective.
|
||||
* @brief Construct an abstraction representing a next line in the stack sequence of
|
||||
* policies, from the caller's perspective.
|
||||
*
|
||||
* @param index An sequential index of this policy in the stack sequence of policies.
|
||||
* @param policies A vector of unique pointers next in the line to be invoked after the current
|
||||
* policy.
|
||||
* @param policies A vector of unique pointers next in the line to be invoked after the
|
||||
* current policy.
|
||||
*/
|
||||
explicit NextHttpPolicy(
|
||||
std::size_t index,
|
||||
@ -138,8 +140,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
* @brief Set the #Azure::Core::Http::HttpTransport that the transport policy will use to send
|
||||
* and receive requests and responses over the wire.
|
||||
*
|
||||
* @remark When no option is set, the default transport adapter on non-Windows platforms is the
|
||||
* curl transport adapter and winhttp transport adapter on Windows.
|
||||
* @remark When no option is set, the default transport adapter on non-Windows platforms is
|
||||
* the curl transport adapter and winhttp transport adapter on Windows.
|
||||
*
|
||||
* @remark When using a custom transport adapter, the implementation for
|
||||
* `AzureSdkGetCustomHttpTransport` must be linked in the end-user application.
|
||||
@ -201,7 +203,7 @@ namespace Azure { namespace Core { namespace Http {
|
||||
/**
|
||||
* @brief HTTP status codes to retry on.
|
||||
*/
|
||||
std::vector<HttpStatusCode> StatusCodes{
|
||||
std::set<HttpStatusCode> StatusCodes{
|
||||
HttpStatusCode::RequestTimeout,
|
||||
HttpStatusCode::InternalServerError,
|
||||
HttpStatusCode::BadGateway,
|
||||
@ -239,9 +241,9 @@ namespace Azure { namespace Core { namespace Http {
|
||||
* @brief Get the Retry Count from the context.
|
||||
*
|
||||
* @remark The sentinel `-1` is returned if there is no information in the \p Context about
|
||||
* #RetryPolicy is trying to send a request. Then `0` is returned for the first try of sending a
|
||||
* request by the #RetryPolicy. Any subsequent retry will be referenced with a number greater
|
||||
* than 0.
|
||||
* #RetryPolicy is trying to send a request. Then `0` is returned for the first try of sending
|
||||
* a request by the #RetryPolicy. Any subsequent retry will be referenced with a number
|
||||
* greater than 0.
|
||||
*
|
||||
* @param context The context used to call send request.
|
||||
* @return A positive number indicating the current intent to send the request.
|
||||
@ -252,8 +254,8 @@ namespace Azure { namespace Core { namespace Http {
|
||||
/**
|
||||
* @brief HTTP Request ID policy.
|
||||
*
|
||||
* @details Applies an HTTP header with a unique ID to each HTTP request, so that each individual
|
||||
* request can be traced for troubleshooting.
|
||||
* @details Applies an HTTP header with a unique ID to each HTTP request, so that each
|
||||
* individual request can be traced for troubleshooting.
|
||||
*/
|
||||
class RequestIdPolicy : public HttpPolicy {
|
||||
private:
|
||||
@ -390,22 +392,44 @@ namespace Azure { namespace Core { namespace Http {
|
||||
NextHttpPolicy policy) const override;
|
||||
};
|
||||
|
||||
namespace Details {
|
||||
AZ_CORE_DLLEXPORT extern Azure::Core::CaseInsensitiveSet g_defaultAllowedHttpHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Options for Azure::Core::Http::LogPolicy.
|
||||
*/
|
||||
struct LogOptions
|
||||
{
|
||||
/**
|
||||
* @brief HTTP query parameters that are allowed to be logged.
|
||||
*/
|
||||
std::set<std::string> AllowedHttpQueryParameters;
|
||||
|
||||
/**
|
||||
* @brief HTTP headers that are allowed to be logged.
|
||||
*/
|
||||
Azure::Core::CaseInsensitiveSet AllowedHttpHeaders = Details::g_defaultAllowedHttpHeaders;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Logs every HTTP request.
|
||||
*
|
||||
* @details Logs every HTTP request, response, or retry attempt.
|
||||
* @remark See #logging.hpp
|
||||
* @details Logs every HTTP request and response.
|
||||
* @remark See Azure::Core::Logger.
|
||||
*/
|
||||
class LoggingPolicy : public HttpPolicy {
|
||||
class LogPolicy : public HttpPolicy {
|
||||
LogOptions m_options;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs HTTP logging policy.
|
||||
*/
|
||||
explicit LoggingPolicy() {}
|
||||
explicit LogPolicy(LogOptions options) : m_options(std::move(options)) {}
|
||||
|
||||
std::unique_ptr<HttpPolicy> Clone() const override
|
||||
{
|
||||
return std::make_unique<LoggingPolicy>(*this);
|
||||
return std::make_unique<LogPolicy>(*this);
|
||||
}
|
||||
|
||||
std::unique_ptr<RawResponse> Send(
|
||||
|
||||
@ -48,7 +48,8 @@ namespace Azure { namespace Core { namespace Internal {
|
||||
*
|
||||
*/
|
||||
ClientOptions(ClientOptions const& options)
|
||||
: Retry(options.Retry), Transport(options.Transport), Telemetry(options.Telemetry)
|
||||
: Retry(options.Retry), Transport(options.Transport), Telemetry(options.Telemetry),
|
||||
Log(options.Log)
|
||||
{
|
||||
PerOperationPolicies.reserve(options.PerOperationPolicies.size());
|
||||
for (auto& policy : options.PerOperationPolicies)
|
||||
@ -78,6 +79,12 @@ namespace Azure { namespace Core { namespace Internal {
|
||||
* @brief Telemetry options.
|
||||
*/
|
||||
Azure::Core::Http::TelemetryOptions Telemetry;
|
||||
|
||||
/**
|
||||
* @brief Define the information to be used for logging.
|
||||
*
|
||||
*/
|
||||
Azure::Core::Http::LogOptions Log;
|
||||
};
|
||||
|
||||
}}} // namespace Azure::Core::Internal
|
||||
|
||||
@ -82,7 +82,7 @@ namespace Azure { namespace Core { namespace Internal { namespace Http {
|
||||
// - TelemetryPolicy
|
||||
// - RequestIdPolicy
|
||||
// - RetryPolicy
|
||||
// - LoggingPolicy
|
||||
// - LogPolicy
|
||||
// - TransportPolicy
|
||||
auto pipelineSize = perCallClientPolicies.size() + perRetryClientPolicies.size()
|
||||
+ perRetryPolicies.size() + perCallPolicies.size() + 5;
|
||||
@ -122,7 +122,7 @@ namespace Azure { namespace Core { namespace Internal { namespace Http {
|
||||
}
|
||||
|
||||
// logging - won't update request
|
||||
m_policies.emplace_back(std::make_unique<Azure::Core::Http::LoggingPolicy>());
|
||||
m_policies.emplace_back(std::make_unique<Azure::Core::Http::LogPolicy>(clientOptions.Log));
|
||||
|
||||
// transport
|
||||
m_policies.emplace_back(
|
||||
|
||||
@ -3,9 +3,37 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "azure/core/logging/logging.hpp"
|
||||
#include "azure/core/dll_import_export.hpp"
|
||||
#include "azure/core/logger.hpp"
|
||||
|
||||
namespace Azure { namespace Core { namespace Logging { namespace Internal {
|
||||
bool ShouldLog(LogLevel level);
|
||||
void Log(LogLevel level, std::string const& message);
|
||||
}}}} // namespace Azure::Core::Logging::Internal
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Azure { namespace Core { namespace Internal {
|
||||
class Log {
|
||||
using LogLevelInt = std::underlying_type<Logger::Level>::type;
|
||||
|
||||
static_assert(
|
||||
std::is_same<int, LogLevelInt>::value == true && ATOMIC_INT_LOCK_FREE == 2,
|
||||
"Logger::Level values must be representable as lock-free");
|
||||
|
||||
static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "atomic<bool> must be lock-free");
|
||||
|
||||
static AZ_CORE_DLLEXPORT std::atomic<bool> g_isLoggingEnabled;
|
||||
static AZ_CORE_DLLEXPORT std::atomic<LogLevelInt> g_logLevel;
|
||||
|
||||
Log() = delete;
|
||||
~Log() = delete;
|
||||
|
||||
public:
|
||||
static bool ShouldWrite(Logger::Level level)
|
||||
{
|
||||
return g_isLoggingEnabled && static_cast<LogLevelInt>(level) >= g_logLevel;
|
||||
}
|
||||
|
||||
static void Write(Logger::Level level, std::string const& message);
|
||||
|
||||
static void EnableLogging(bool isEnabled);
|
||||
static void SetLogLevel(Logger::Level logLevel);
|
||||
};
|
||||
}}} // namespace Azure::Core::Internal
|
||||
|
||||
68
sdk/core/azure-core/inc/azure/core/logger.hpp
Normal file
68
sdk/core/azure-core/inc/azure/core/logger.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief This header defines the types and functions your application uses to be notified of Azure
|
||||
* SDK client library log messages.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Core {
|
||||
/**
|
||||
* @brief Log message handler.
|
||||
*/
|
||||
class Logger {
|
||||
public:
|
||||
/**
|
||||
* @brief Log message level.
|
||||
*/
|
||||
// https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/core/azure-core/src/main/java/com/azure/core/util/logging/LogLevel.java
|
||||
enum class Level : int
|
||||
{
|
||||
/// Logging level for detailed troubleshooting scenarios.
|
||||
Verbose = 1,
|
||||
|
||||
/// Logging level when a function operates normally.
|
||||
Informational = 2,
|
||||
|
||||
/// Logging level when a function fails to perform its intended task.
|
||||
Warning = 3,
|
||||
|
||||
/// Logging level for failures that the application is unlikely to recover from.
|
||||
Error = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Defines the signature of the callback function that application developers must write
|
||||
* in order to receive Azure SDK log messages.
|
||||
*
|
||||
* @param level The log message level.
|
||||
* @param message The log message.
|
||||
*/
|
||||
typedef std::function<void(Level level, std::string const& message)> Listener;
|
||||
|
||||
/**
|
||||
* @brief Set the function that will be invoked to report an SDK log message.
|
||||
*
|
||||
* @param listener An #Azure::Core::Logger::Listener function that will be invoked when
|
||||
* the SDK reports a log message. If `nullptr`, no function will be invoked.
|
||||
*/
|
||||
static void SetListener(Listener listener);
|
||||
|
||||
/**
|
||||
* @brief Sets the #Azure::Core::Logger::Level an application is interested in receiving.
|
||||
*
|
||||
* @param level Maximum log level.
|
||||
*/
|
||||
static void SetLevel(Level level);
|
||||
|
||||
private:
|
||||
Logger() = delete;
|
||||
~Logger() = delete;
|
||||
};
|
||||
}} // namespace Azure::Core
|
||||
@ -1,58 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief This header defines the types and functions your application uses to be notified of Azure
|
||||
* SDK client library log messages.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Core { namespace Logging {
|
||||
/**
|
||||
* @brief Log message level.
|
||||
*/
|
||||
enum class LogLevel
|
||||
{
|
||||
/// Logging level for failures that the application is unlikely to recover from.
|
||||
Error,
|
||||
|
||||
/// Logging level when a function fails to perform its intended task.
|
||||
Warning,
|
||||
|
||||
/// Logging level when a function operates normally.
|
||||
Informational,
|
||||
|
||||
/// Logging level for detailed troubleshooting scenarios.
|
||||
Verbose,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Defines the signature of the callback function that application developers must write in
|
||||
* order to receive Azure SDK log messages.
|
||||
*
|
||||
* @param level The log message level.
|
||||
* @param message The log message.
|
||||
*/
|
||||
typedef std::function<void(LogLevel level, std::string const& message)> LogListener;
|
||||
|
||||
/**
|
||||
* @brief Set the function that will be invoked to report an SDK log message.
|
||||
*
|
||||
* @param logListener A #Azure::Core::Logging::LogListener function that will be invoked when the
|
||||
* SDK reports a log message matching one of the log levels passed to
|
||||
* #Azure::Core::Logging::SetLogLevel(). If `nullptr`, no function will be invoked.
|
||||
*/
|
||||
void SetLogListener(LogListener logListener);
|
||||
|
||||
/**
|
||||
* @brief Sets the #Azure::Core::Logging::LogLevel an application is interested in receiving.
|
||||
*
|
||||
* @param level Maximum log level.
|
||||
*/
|
||||
void SetLogLevel(LogLevel level);
|
||||
}}} // namespace Azure::Core::Logging
|
||||
@ -73,11 +73,16 @@ constexpr bool IsLeapYear(int16_t year)
|
||||
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
|
||||
}
|
||||
|
||||
constexpr int16_t LeapYarsSinceEpoch(int16_t year)
|
||||
constexpr int16_t LeapYearsSinceEpoch(int16_t year)
|
||||
{
|
||||
int16_t const fourHundredYearPeriods = year / 400;
|
||||
int16_t const hundredYearPeriods = (year % 400) / 100;
|
||||
int16_t const remainder = year - (400 * fourHundredYearPeriods) - 100 * (hundredYearPeriods);
|
||||
|
||||
// Every 4 years have 1 leap year.
|
||||
// Every 100 years have 24, not 25 leap years (years divisible by 100 are not leap years).
|
||||
// Every 400 years have 97, not 96 (would be 24 * 4 = 96) years.
|
||||
// That's because year divisible by 100 is not a leap year, unless it is also divisible by 400.
|
||||
return (fourHundredYearPeriods * 97) + (hundredYearPeriods * 24) + (remainder / 4);
|
||||
}
|
||||
|
||||
@ -102,7 +107,7 @@ constexpr int16_t DayOfYear(int16_t year, int8_t month, int8_t day)
|
||||
constexpr int32_t DaySinceEpoch(int16_t year, int8_t month, int8_t day)
|
||||
{
|
||||
int16_t const priorYear = year - 1;
|
||||
auto const leapYears = LeapYarsSinceEpoch(priorYear);
|
||||
auto const leapYears = LeapYearsSinceEpoch(priorYear);
|
||||
auto const nonLeapYears = priorYear - leapYears;
|
||||
return (nonLeapYears * 365) + (leapYears * 366) + DayOfYear(year, month, day);
|
||||
}
|
||||
@ -112,11 +117,17 @@ constexpr int8_t GetDayOfWeek(int16_t year, int8_t month, int8_t day)
|
||||
return DaySinceEpoch(year, month, day) % 7;
|
||||
}
|
||||
|
||||
constexpr int8_t WeekDayMonthDayOfYear(int8_t* month, int8_t* day, int16_t year, int16_t dayOfYear)
|
||||
constexpr int8_t WeekDayAndMonthDayOfYear(
|
||||
int8_t* month,
|
||||
int8_t* day,
|
||||
int16_t year,
|
||||
int16_t dayOfYear)
|
||||
{
|
||||
auto remainder = dayOfYear;
|
||||
for (int8_t i = 1; i <= 12; ++i)
|
||||
{
|
||||
// MonthDays = number of days in this month.
|
||||
// If this month is February, then check for leap year and adjust accordingly.
|
||||
int8_t const MonthDays = MaxDaysPerMonth[i - 1] - ((i == 2 && !IsLeapYear(year)) ? 1 : 0);
|
||||
if (remainder <= MonthDays)
|
||||
{
|
||||
@ -780,7 +791,7 @@ std::string DateTime::ToString(DateFormat format, TimeFractionFormat fractionFor
|
||||
|
||||
year += static_cast<int16_t>((years400 * 400) + (years100 * 100) + (years4 * 4) + years1);
|
||||
|
||||
dayOfWeek = WeekDayMonthDayOfYear(
|
||||
dayOfWeek = WeekDayAndMonthDayOfYear(
|
||||
&month,
|
||||
&day,
|
||||
year,
|
||||
|
||||
148
sdk/core/azure-core/src/environment_log_level_listener.cpp
Normal file
148
sdk/core/azure-core/src/environment_log_level_listener.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#if defined(AZ_NO_ENV_LOGGER)
|
||||
#include "azure/core/platform.hpp"
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#if !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(AZ_NO_ENV_LOGGER) \
|
||||
&& (!defined(WINAPI_PARTITION_DESKTOP) \
|
||||
|| WINAPI_PARTITION_DESKTOP) // See azure/core/platform.hpp for explanation.
|
||||
|
||||
#include "environment_log_level_listener_private.hpp"
|
||||
|
||||
#include "azure/core/datetime.hpp"
|
||||
#include "azure/core/internal/strings.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace Azure::Core;
|
||||
using namespace Azure::Core::Details;
|
||||
|
||||
namespace {
|
||||
Logger::Level const* GetEnvironmentLogLevel()
|
||||
{
|
||||
static Logger::Level* envLogLevelPtr = nullptr;
|
||||
|
||||
static bool initialized = false;
|
||||
if (!initialized)
|
||||
{
|
||||
initialized = true;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
// warning C4996: 'getenv': This function or variable may be unsafe. Consider using _dupenv_s
|
||||
// instead.
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
auto envVar = std::getenv("AZURE_LOG_LEVEL");
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
if (envVar)
|
||||
{
|
||||
auto const logLevelStr = Internal::Strings::ToLower(envVar);
|
||||
|
||||
// See https://github.com/Azure/azure-sdk-for-java/wiki/Logging-with-Azure-SDK
|
||||
// And
|
||||
// https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/core/azure-core/src/main/java/com/azure/core/util/logging/LogLevel.java
|
||||
|
||||
static Logger::Level envLogLevel = {};
|
||||
envLogLevelPtr = &envLogLevel;
|
||||
|
||||
if (logLevelStr == "error" || logLevelStr == "err" || logLevelStr == "4")
|
||||
{
|
||||
envLogLevel = Logger::Level::Error;
|
||||
}
|
||||
else if (logLevelStr == "warning" || logLevelStr == "warn" || logLevelStr == "3")
|
||||
{
|
||||
envLogLevel = Logger::Level::Warning;
|
||||
}
|
||||
else if (
|
||||
logLevelStr == "informational" || logLevelStr == "info" || logLevelStr == "information"
|
||||
|| logLevelStr == "2")
|
||||
{
|
||||
envLogLevel = Logger::Level::Informational;
|
||||
}
|
||||
else if (logLevelStr == "verbose" || logLevelStr == "debug" || logLevelStr == "1")
|
||||
{
|
||||
envLogLevel = Logger::Level::Verbose;
|
||||
}
|
||||
else
|
||||
{
|
||||
envLogLevelPtr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return envLogLevelPtr;
|
||||
}
|
||||
|
||||
// Log level textual representation, including space padding, matches slf4j and log4net.
|
||||
std::string const ErrorText = "ERROR";
|
||||
std::string const WarningText = "WARN ";
|
||||
std::string const InformationalText = "INFO ";
|
||||
std::string const VerboseText = "DEBUG";
|
||||
std::string const UnknownText = "?????";
|
||||
|
||||
inline std::string const& LogLevelToConsoleString(Logger::Level logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case Logger::Level::Error:
|
||||
return ErrorText;
|
||||
|
||||
case Logger::Level::Warning:
|
||||
return WarningText;
|
||||
|
||||
case Logger::Level::Informational:
|
||||
return InformationalText;
|
||||
|
||||
case Logger::Level::Verbose:
|
||||
return VerboseText;
|
||||
|
||||
default:
|
||||
return UnknownText;
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Logger::Level EnvironmentLogLevelListener::GetLogLevel(Logger::Level defaultValue)
|
||||
{
|
||||
auto const envLogLevelPtr = GetEnvironmentLogLevel();
|
||||
return envLogLevelPtr ? *envLogLevelPtr : defaultValue;
|
||||
}
|
||||
|
||||
Logger::Listener EnvironmentLogLevelListener::GetLogListener()
|
||||
{
|
||||
bool const isEnvLogLevelSet = GetEnvironmentLogLevel() != nullptr;
|
||||
if (!isEnvLogLevelSet)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Logger::Listener const consoleLogger = [](auto level, auto message) {
|
||||
std::cerr << '['
|
||||
<< Azure::Core::DateTime(std::chrono::system_clock::now())
|
||||
.ToString(
|
||||
DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits)
|
||||
<< "] " << LogLevelToConsoleString(level) << " : " << message << std::endl;
|
||||
};
|
||||
|
||||
return consoleLogger;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "azure/core/logger.hpp"
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#if !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Azure { namespace Core { namespace Details {
|
||||
|
||||
class EnvironmentLogLevelListener {
|
||||
EnvironmentLogLevelListener() = delete;
|
||||
~EnvironmentLogLevelListener() = delete;
|
||||
|
||||
public:
|
||||
static Logger::Level GetLogLevel(Logger::Level defaultValue);
|
||||
static Logger::Listener GetLogListener();
|
||||
};
|
||||
|
||||
#if defined(AZ_NO_ENV_LOGGER) \
|
||||
|| (defined(WINAPI_PARTITION_DESKTOP) \
|
||||
&& !WINAPI_PARTITION_DESKTOP) // See azure/core/platform.hpp for explanation.
|
||||
inline Logger::Level EnvironmentLogLevelListener::GetLogLevel(Logger::Level defaultValue)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
inline Logger::Listener EnvironmentLogLevelListener::GetLogListener() { return nullptr; }
|
||||
#endif
|
||||
}}} // namespace Azure::Core::Details
|
||||
@ -110,6 +110,9 @@ int pollSocketUntilEventOrTimeout(
|
||||
return result;
|
||||
}
|
||||
|
||||
using Azure::Core::Logger;
|
||||
using Azure::Core::Internal::Log;
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
// Windows needs this after every write to socket or performance would be reduced to 1/4 for
|
||||
// uploading operation.
|
||||
@ -127,15 +130,12 @@ void WinSocketSetBuffSize(curl_socket_t socket)
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-setsockopt
|
||||
auto result = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (const char*)&ideal, sizeof(ideal));
|
||||
|
||||
if (Log::ShouldWrite(Logger::Level::Verbose))
|
||||
{
|
||||
using namespace Azure::Core::Logging;
|
||||
using namespace Azure::Core::Logging::Internal;
|
||||
if (ShouldLog(LogLevel::Verbose))
|
||||
{
|
||||
Log(LogLevel::Verbose,
|
||||
LogMsgPrefix + "Windows - calling setsockopt after uploading chunk. ideal = "
|
||||
+ std::to_string(ideal) + " result = " + std::to_string(result));
|
||||
}
|
||||
Log::Write(
|
||||
Logger::Level::Verbose,
|
||||
LogMsgPrefix + "Windows - calling setsockopt after uploading chunk. ideal = "
|
||||
+ std::to_string(ideal) + " result = " + std::to_string(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,11 +156,8 @@ using Azure::Core::Http::TransportException;
|
||||
|
||||
std::unique_ptr<RawResponse> CurlTransport::Send(Context const& context, Request& request)
|
||||
{
|
||||
using namespace Azure::Core::Logging;
|
||||
using namespace Azure::Core::Logging::Internal;
|
||||
|
||||
// Create CurlSession to perform request
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Creating a new session.");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Creating a new session.");
|
||||
|
||||
auto session = std::make_unique<CurlSession>(
|
||||
request, CurlConnectionPool::GetCurlConnection(request, m_options), m_options.HttpKeepAlive);
|
||||
@ -192,7 +189,8 @@ std::unique_ptr<RawResponse> CurlTransport::Send(Context const& context, Request
|
||||
"Error while sending request. " + std::string(curl_easy_strerror(performing)));
|
||||
}
|
||||
|
||||
Log(LogLevel::Verbose,
|
||||
Log::Write(
|
||||
Logger::Level::Verbose,
|
||||
LogMsgPrefix + "Request completed. Moving response out of session and session to response.");
|
||||
|
||||
// Move Response out of the session
|
||||
@ -204,9 +202,6 @@ std::unique_ptr<RawResponse> CurlTransport::Send(Context const& context, Request
|
||||
|
||||
CURLcode CurlSession::Perform(Context const& context)
|
||||
{
|
||||
using namespace Azure::Core::Logging;
|
||||
using namespace Azure::Core::Logging::Internal;
|
||||
|
||||
// Set the session state
|
||||
m_sessionState = SessionState::PERFORM;
|
||||
|
||||
@ -216,13 +211,13 @@ CURLcode CurlSession::Perform(Context const& context)
|
||||
auto hostHeader = headers.find("Host");
|
||||
if (hostHeader == headers.end())
|
||||
{
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "No Host in request headers. Adding it");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "No Host in request headers. Adding it");
|
||||
this->m_request.SetHeader("Host", this->m_request.GetUrl().GetHost());
|
||||
}
|
||||
auto isContentLengthHeaderInRequest = headers.find("content-length");
|
||||
if (isContentLengthHeaderInRequest == headers.end())
|
||||
{
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "No content-length in headers. Adding it");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "No content-length in headers. Adding it");
|
||||
this->m_request.SetHeader(
|
||||
"content-length", std::to_string(this->m_request.GetBodyStream()->Length()));
|
||||
}
|
||||
@ -231,14 +226,14 @@ CURLcode CurlSession::Perform(Context const& context)
|
||||
// use expect:100 for PUT requests. Server will decide if it can take our request
|
||||
if (this->m_request.GetMethod() == HttpMethod::Put)
|
||||
{
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Using 100-continue for PUT request");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Using 100-continue for PUT request");
|
||||
this->m_request.SetHeader("expect", "100-continue");
|
||||
}
|
||||
|
||||
// Send request. If the connection assigned to this curlSession is closed or the socket is
|
||||
// somehow lost, libcurl will return CURLE_UNSUPPORTED_PROTOCOL
|
||||
// (https://curl.haxx.se/libcurl/c/curl_easy_send.html). Return the error back.
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Send request without payload");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Send request without payload");
|
||||
|
||||
auto result = SendRawHttp(context);
|
||||
if (result != CURLE_OK)
|
||||
@ -246,7 +241,7 @@ CURLcode CurlSession::Perform(Context const& context)
|
||||
return result;
|
||||
}
|
||||
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Parse server response");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Parse server response");
|
||||
ReadStatusLineAndHeadersFromRawResponse(context);
|
||||
|
||||
// non-PUT request are ready to be stream at this point. Only PUT request would start an uploading
|
||||
@ -257,17 +252,17 @@ CURLcode CurlSession::Perform(Context const& context)
|
||||
return result;
|
||||
}
|
||||
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Check server response before upload starts");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Check server response before upload starts");
|
||||
// Check server response from Expect:100-continue for PUT;
|
||||
// This help to prevent us from start uploading data when Server can't handle it
|
||||
if (this->m_lastStatusCode != HttpStatusCode::Continue)
|
||||
{
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Server rejected the upload request");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Server rejected the upload request");
|
||||
m_sessionState = SessionState::STREAMING;
|
||||
return result; // Won't upload.
|
||||
}
|
||||
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Upload payload");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Upload payload");
|
||||
if (this->m_bodyStartInBuffer > 0)
|
||||
{
|
||||
// If internal buffer has more data after the 100-continue means Server return an error.
|
||||
@ -285,7 +280,7 @@ CURLcode CurlSession::Perform(Context const& context)
|
||||
return result; // will throw transport exception before trying to read
|
||||
}
|
||||
|
||||
Log(LogLevel::Verbose, LogMsgPrefix + "Upload completed. Parse server response");
|
||||
Log::Write(Logger::Level::Verbose, LogMsgPrefix + "Upload completed. Parse server response");
|
||||
ReadStatusLineAndHeadersFromRawResponse(context);
|
||||
// If no throw at this point, the request is ready to stream.
|
||||
// If any throw happened before this point, the state will remain as PERFORM.
|
||||
|
||||
146
sdk/core/azure-core/src/http/log_policy.cpp
Normal file
146
sdk/core/azure-core/src/http/log_policy.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/core/http/policy.hpp"
|
||||
#include "azure/core/internal/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
|
||||
using Azure::Core::Context;
|
||||
using namespace Azure::Core::Http;
|
||||
|
||||
namespace {
|
||||
std::string RedactedPlaceholder = "REDACTED";
|
||||
|
||||
inline void AppendHeaders(
|
||||
std::ostringstream& log,
|
||||
Azure::Core::CaseInsensitiveMap const& headers,
|
||||
Azure::Core::CaseInsensitiveSet const& allowedHaders)
|
||||
{
|
||||
for (auto const& header : headers)
|
||||
{
|
||||
log << std::endl << header.first << " : ";
|
||||
|
||||
if (!header.second.empty())
|
||||
{
|
||||
log
|
||||
<< ((allowedHaders.find(header.first) != allowedHaders.end()) ? header.second
|
||||
: RedactedPlaceholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string GetRequestLogMessage(
|
||||
Azure::Core::Http::LogOptions const& options,
|
||||
Request const& request)
|
||||
{
|
||||
auto const& requestUrl = request.GetUrl();
|
||||
|
||||
std::ostringstream log;
|
||||
log << "HTTP Request : " << HttpMethodToString(request.GetMethod()) << " "
|
||||
<< requestUrl.GetUrlWithoutQuery();
|
||||
{
|
||||
auto encodedRequestQueryParams = requestUrl.GetQueryParameters();
|
||||
auto const& unencodedAllowedQueryParams = options.AllowedHttpQueryParameters;
|
||||
if (!encodedRequestQueryParams.empty() && !unencodedAllowedQueryParams.empty())
|
||||
{
|
||||
std::remove_const<std::remove_reference<decltype(unencodedAllowedQueryParams)>::type>::type
|
||||
encodedAllowedQueryParams;
|
||||
std::transform(
|
||||
unencodedAllowedQueryParams.begin(),
|
||||
unencodedAllowedQueryParams.end(),
|
||||
std::inserter(encodedAllowedQueryParams, encodedAllowedQueryParams.begin()),
|
||||
[](std::string const& s) { return Url::Encode(s); });
|
||||
|
||||
std::remove_const<std::remove_reference<decltype(encodedRequestQueryParams)>::type>::type
|
||||
encodedAllowedRequestQueryParams;
|
||||
for (auto const& encodedRequestQueryParam : encodedRequestQueryParams)
|
||||
{
|
||||
if (encodedRequestQueryParam.second.empty()
|
||||
|| (encodedAllowedQueryParams.find(encodedRequestQueryParam.first)
|
||||
!= encodedAllowedQueryParams.end()))
|
||||
{
|
||||
encodedAllowedRequestQueryParams.insert(encodedRequestQueryParam);
|
||||
}
|
||||
else
|
||||
{
|
||||
encodedAllowedRequestQueryParams.insert(
|
||||
std::make_pair(encodedRequestQueryParam.first, RedactedPlaceholder));
|
||||
}
|
||||
}
|
||||
|
||||
log << Details::FormatEncodedUrlQueryParameters(encodedAllowedRequestQueryParams);
|
||||
}
|
||||
}
|
||||
AppendHeaders(log, request.GetHeaders(), options.AllowedHttpHeaders);
|
||||
return log.str();
|
||||
}
|
||||
|
||||
inline std::string GetResponseLogMessage(
|
||||
Azure::Core::Http::LogOptions const& options,
|
||||
RawResponse const& response,
|
||||
std::chrono::system_clock::duration const& duration)
|
||||
{
|
||||
std::ostringstream log;
|
||||
|
||||
log << "HTTP Response ("
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()
|
||||
<< "ms) : " << static_cast<int>(response.GetStatusCode()) << " "
|
||||
<< response.GetReasonPhrase();
|
||||
|
||||
AppendHeaders(log, response.GetHeaders(), options.AllowedHttpHeaders);
|
||||
return log.str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Azure::Core::CaseInsensitiveSet Azure::Core::Http::Details::g_defaultAllowedHttpHeaders
|
||||
= {"x-ms-client-request-id",
|
||||
"x-ms-return-client-request-id",
|
||||
"traceparent",
|
||||
"Accept",
|
||||
"Cache-Control",
|
||||
"Connection",
|
||||
"Content-Length",
|
||||
"Content-Type",
|
||||
"Date",
|
||||
"ETag",
|
||||
"Expires",
|
||||
"If-Match",
|
||||
"If-Modified-Since",
|
||||
"If-None-Match",
|
||||
"If-Unmodified-Since",
|
||||
"Last-Modified",
|
||||
"Pragma",
|
||||
"Request-Id",
|
||||
"Retry-After",
|
||||
"Server",
|
||||
"Transfer-Encoding",
|
||||
"User-Agent"};
|
||||
|
||||
std::unique_ptr<RawResponse> Azure::Core::Http::LogPolicy::Send(
|
||||
Context const& ctx,
|
||||
Request& request,
|
||||
NextHttpPolicy nextHttpPolicy) const
|
||||
{
|
||||
using Azure::Core::Internal::Log;
|
||||
|
||||
if (Log::ShouldWrite(Logger::Level::Verbose))
|
||||
{
|
||||
Log::Write(Logger::Level::Informational, GetRequestLogMessage(m_options, request));
|
||||
}
|
||||
else
|
||||
{
|
||||
return nextHttpPolicy.Send(ctx, request);
|
||||
}
|
||||
|
||||
auto const start = std::chrono::system_clock::now();
|
||||
auto response = nextHttpPolicy.Send(ctx, request);
|
||||
auto const end = std::chrono::system_clock::now();
|
||||
|
||||
Log::Write(
|
||||
Logger::Level::Informational, GetResponseLogMessage(m_options, *response, end - start));
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/core/http/policy.hpp"
|
||||
#include "azure/core/internal/log.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
|
||||
using Azure::Core::Context;
|
||||
using namespace Azure::Core::Http;
|
||||
|
||||
namespace {
|
||||
std::string TruncateIfLengthy(std::string const& s)
|
||||
{
|
||||
static constexpr auto const MaxLength = 50;
|
||||
|
||||
auto const length = s.length();
|
||||
if (length <= MaxLength)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
static constexpr char const Ellipsis[] = " ... ";
|
||||
static constexpr auto const EllipsisLength = sizeof(Ellipsis) - 1;
|
||||
|
||||
auto const BeginLength = (MaxLength / 2) - ((EllipsisLength / 2) + (EllipsisLength % 2));
|
||||
auto const EndLength = ((MaxLength / 2) + (MaxLength % 2)) - (EllipsisLength / 2);
|
||||
|
||||
return s.substr(0, BeginLength) + Ellipsis + s.substr(length - EndLength, EndLength);
|
||||
}
|
||||
|
||||
std::string GetRequestLogMessage(Request const& request)
|
||||
{
|
||||
std::ostringstream log;
|
||||
|
||||
log << "HTTP Request : " << HttpMethodToString(request.GetMethod()) << " "
|
||||
<< request.GetUrl().GetAbsoluteUrl();
|
||||
|
||||
for (auto header : request.GetHeaders())
|
||||
{
|
||||
log << "\n\t" << header.first << " : " << TruncateIfLengthy(header.second);
|
||||
}
|
||||
|
||||
return log.str();
|
||||
}
|
||||
|
||||
std::string GetResponseLogMessage(
|
||||
Request const& request,
|
||||
RawResponse const& response,
|
||||
std::chrono::system_clock::duration const& duration)
|
||||
{
|
||||
std::ostringstream log;
|
||||
|
||||
log << "HTTP Response ("
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()
|
||||
<< "ms) : " << static_cast<int>(response.GetStatusCode()) << " "
|
||||
<< response.GetReasonPhrase();
|
||||
|
||||
for (auto header : response.GetHeaders())
|
||||
{
|
||||
log << "\n\t" << header.first;
|
||||
if (!header.second.empty() && header.first != "authorization")
|
||||
{
|
||||
log << " : " << TruncateIfLengthy(header.second);
|
||||
}
|
||||
}
|
||||
|
||||
log << "\n\n -> " << GetRequestLogMessage(request);
|
||||
|
||||
return log.str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<RawResponse> Azure::Core::Http::LoggingPolicy::Send(
|
||||
Context const& ctx,
|
||||
Request& request,
|
||||
NextHttpPolicy nextHttpPolicy) const
|
||||
{
|
||||
if (Logging::Internal::ShouldLog(Logging::LogLevel::Verbose))
|
||||
{
|
||||
Logging::Internal::Log(Logging::LogLevel::Verbose, GetRequestLogMessage(request));
|
||||
}
|
||||
else
|
||||
{
|
||||
return nextHttpPolicy.Send(ctx, request);
|
||||
}
|
||||
|
||||
auto const start = std::chrono::system_clock::now();
|
||||
auto response = nextHttpPolicy.Send(ctx, request);
|
||||
auto const end = std::chrono::system_clock::now();
|
||||
|
||||
Logging::Internal::Log(
|
||||
Logging::LogLevel::Verbose, GetResponseLogMessage(request, *response, end - start));
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -101,6 +101,8 @@ bool ShouldRetryOnResponse(
|
||||
RetryNumber attempt,
|
||||
Delay& retryAfter)
|
||||
{
|
||||
using Azure::Core::Logger;
|
||||
using Azure::Core::Internal::Log;
|
||||
// Are we out of retry attempts?
|
||||
if (WasLastAttempt(retryOptions, attempt))
|
||||
{
|
||||
@ -108,11 +110,28 @@ bool ShouldRetryOnResponse(
|
||||
}
|
||||
|
||||
// Should we retry on the given response retry code?
|
||||
auto const& statusCodes = retryOptions.StatusCodes;
|
||||
auto const statusCodesEnd = statusCodes.end();
|
||||
if (std::find(statusCodes.begin(), statusCodesEnd, response.GetStatusCode()) == statusCodesEnd)
|
||||
{
|
||||
return false;
|
||||
auto const& statusCodes = retryOptions.StatusCodes;
|
||||
auto const sc = response.GetStatusCode();
|
||||
if (statusCodes.find(sc) == statusCodes.end())
|
||||
{
|
||||
if (Log::ShouldWrite(Logger::Level::Warning))
|
||||
{
|
||||
Log::Write(
|
||||
Logger::Level::Warning,
|
||||
std::string("HTTP status code ") + std::to_string(static_cast<int>(sc))
|
||||
+ " won't be retried.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (Log::ShouldWrite(Logger::Level::Informational))
|
||||
{
|
||||
Log::Write(
|
||||
Logger::Level::Informational,
|
||||
std::string("HTTP status code ") + std::to_string(static_cast<int>(sc))
|
||||
+ " will be retried.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetResponseHeaderBasedDelay(response, retryAfter))
|
||||
@ -163,8 +182,11 @@ std::unique_ptr<RawResponse> Azure::Core::Http::RetryPolicy::Send(
|
||||
Request& request,
|
||||
NextHttpPolicy nextHttpPolicy) const
|
||||
{
|
||||
auto const shouldLog = Logging::Internal::ShouldLog(Logging::LogLevel::Informational);
|
||||
using Azure::Core::Logger;
|
||||
using Azure::Core::Internal::Log;
|
||||
|
||||
auto retryContext = CreateRetryContext(ctx);
|
||||
|
||||
for (RetryNumber attempt = 1;; ++attempt)
|
||||
{
|
||||
Delay retryAfter{};
|
||||
@ -185,22 +207,27 @@ std::unique_ptr<RawResponse> Azure::Core::Http::RetryPolicy::Send(
|
||||
return response;
|
||||
}
|
||||
}
|
||||
catch (const TransportException&)
|
||||
catch (const TransportException& e)
|
||||
{
|
||||
if (Log::ShouldWrite(Logger::Level::Warning))
|
||||
{
|
||||
Log::Write(Logger::Level::Warning, std::string("HTTP Transport error: ") + e.what());
|
||||
}
|
||||
|
||||
if (!ShouldRetryOnTransportFailure(m_retryOptions, attempt, retryAfter))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldLog)
|
||||
if (Log::ShouldWrite(Logger::Level::Informational))
|
||||
{
|
||||
std::ostringstream log;
|
||||
|
||||
log << "HTTP Retry attempt #" << attempt << " will be made in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(retryAfter).count() << "ms.";
|
||||
|
||||
Logging::Internal::Log(Logging::LogLevel::Informational, log.str());
|
||||
Log::Write(Logger::Level::Informational, log.str());
|
||||
}
|
||||
|
||||
// Sleep(0) behavior is implementation-defined: it may yield, or may do nothing. Let's make sure
|
||||
|
||||
@ -177,43 +177,46 @@ void Url::AppendQueryParameters(const std::string& query)
|
||||
}
|
||||
}
|
||||
|
||||
std::string Url::GetRelativeUrl() const
|
||||
std::string Url::GetUrlWithoutQuery(bool relative) const
|
||||
{
|
||||
std::string relative_url;
|
||||
if (!m_encodedPath.empty())
|
||||
std::string url;
|
||||
|
||||
if (!relative)
|
||||
{
|
||||
relative_url += m_encodedPath;
|
||||
}
|
||||
{
|
||||
relative_url += '?';
|
||||
for (const auto& q : m_encodedQueryParameters)
|
||||
if (!m_scheme.empty())
|
||||
{
|
||||
relative_url += q.first + '=' + q.second + '&';
|
||||
url += m_scheme + "://";
|
||||
}
|
||||
url += m_host;
|
||||
if (m_port != 0)
|
||||
{
|
||||
url += ":" + std::to_string(m_port);
|
||||
}
|
||||
relative_url.pop_back();
|
||||
}
|
||||
|
||||
return relative_url;
|
||||
if (!m_encodedPath.empty())
|
||||
{
|
||||
if (!relative)
|
||||
{
|
||||
url += "/";
|
||||
}
|
||||
|
||||
url += m_encodedPath;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
std::string Url::GetRelativeUrl() const
|
||||
{
|
||||
return GetUrlWithoutQuery(true)
|
||||
+ Details::FormatEncodedUrlQueryParameters(m_encodedQueryParameters);
|
||||
}
|
||||
|
||||
std::string Url::GetAbsoluteUrl() const
|
||||
{
|
||||
std::string full_url;
|
||||
if (!m_scheme.empty())
|
||||
{
|
||||
full_url += m_scheme + "://";
|
||||
}
|
||||
full_url += m_host;
|
||||
if (m_port != 0)
|
||||
{
|
||||
full_url += ":" + std::to_string(m_port);
|
||||
}
|
||||
if (!m_encodedPath.empty())
|
||||
{
|
||||
full_url += "/";
|
||||
}
|
||||
full_url += GetRelativeUrl();
|
||||
return full_url;
|
||||
return GetUrlWithoutQuery(false)
|
||||
+ Details::FormatEncodedUrlQueryParameters(m_encodedQueryParameters);
|
||||
}
|
||||
|
||||
const std::unordered_set<unsigned char> Url::defaultNonUrlEncodeChars
|
||||
|
||||
52
sdk/core/azure-core/src/logger.cpp
Normal file
52
sdk/core/azure-core/src/logger.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/core/logger.hpp"
|
||||
#include "azure/core/internal/log.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "environment_log_level_listener_private.hpp"
|
||||
|
||||
using namespace Azure::Core;
|
||||
using namespace Azure::Core::Internal;
|
||||
|
||||
namespace {
|
||||
static std::shared_timed_mutex g_logListenerMutex;
|
||||
static Logger::Listener g_logListener(Details::EnvironmentLogLevelListener::GetLogListener());
|
||||
} // namespace
|
||||
|
||||
std::atomic<bool> Log::g_isLoggingEnabled(
|
||||
Details::EnvironmentLogLevelListener::GetLogListener() != nullptr);
|
||||
|
||||
std::atomic<Log::LogLevelInt> Log::g_logLevel(static_cast<LogLevelInt>(
|
||||
Details::EnvironmentLogLevelListener::GetLogLevel(Logger::Level::Warning)));
|
||||
|
||||
inline void Log::EnableLogging(bool isEnabled) { g_isLoggingEnabled = isEnabled; }
|
||||
|
||||
inline void Log::SetLogLevel(Logger::Level logLevel)
|
||||
{
|
||||
g_logLevel = static_cast<LogLevelInt>(logLevel);
|
||||
}
|
||||
|
||||
void Log::Write(Logger::Level level, std::string const& message)
|
||||
{
|
||||
if (ShouldWrite(level))
|
||||
{
|
||||
std::shared_lock<std::shared_timed_mutex> loggerLock(g_logListenerMutex);
|
||||
if (g_logListener)
|
||||
{
|
||||
g_logListener(level, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::SetListener(Logger::Listener listener)
|
||||
{
|
||||
std::unique_lock<std::shared_timed_mutex> loggerLock(g_logListenerMutex);
|
||||
g_logListener = std::move(listener);
|
||||
Log::EnableLogging(g_logListener != nullptr);
|
||||
}
|
||||
|
||||
void Logger::SetLevel(Logger::Level level) { Log::SetLogLevel(level); }
|
||||
@ -1,47 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/core/logging/logging.hpp"
|
||||
#include "azure/core/internal/log.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
using namespace Azure::Core::Logging;
|
||||
using namespace Azure::Core::Logging::Internal;
|
||||
|
||||
namespace {
|
||||
std::mutex g_loggerMutex;
|
||||
LogListener g_logListener(nullptr);
|
||||
LogLevel g_logLevel = LogLevel::Verbose;
|
||||
|
||||
LogListener GetLogListener(LogLevel level)
|
||||
{
|
||||
std::lock_guard<std::mutex> loggerLock(g_loggerMutex);
|
||||
return level <= g_logLevel ? g_logListener : LogListener(nullptr);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Azure::Core::Logging::SetLogListener(LogListener logListener)
|
||||
{
|
||||
std::lock_guard<std::mutex> loggerLock(g_loggerMutex);
|
||||
g_logListener = std::move(logListener);
|
||||
}
|
||||
|
||||
void Azure::Core::Logging::SetLogLevel(LogLevel level)
|
||||
{
|
||||
std::lock_guard<std::mutex> loggerLock(g_loggerMutex);
|
||||
g_logLevel = level;
|
||||
}
|
||||
|
||||
bool Azure::Core::Logging::Internal::ShouldLog(LogLevel level)
|
||||
{
|
||||
return GetLogListener(level) != nullptr;
|
||||
}
|
||||
|
||||
void Azure::Core::Logging::Internal::Log(LogLevel level, std::string const& message)
|
||||
{
|
||||
if (auto logListener = GetLogListener(level))
|
||||
{
|
||||
logListener(level, message);
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ add_executable (
|
||||
azure-core-test
|
||||
base64.cpp
|
||||
bodystream.cpp
|
||||
case_insensitive_map.cpp
|
||||
case_insensitive_containers.cpp
|
||||
client_options.cpp
|
||||
context.cpp
|
||||
${CURL_CONNECTION_POOL_TESTS}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <azure/core/case_insensitive_map.hpp>
|
||||
#include <azure/core/case_insensitive_containers.hpp>
|
||||
|
||||
using namespace Azure::Core;
|
||||
|
||||
@ -48,7 +48,7 @@ template <DateTime::TimeFractionFormat TF = DateTime::TimeFractionFormat::DropTr
|
||||
void TestDateTimeRoundtrip(std::string const& str, std::string const& strExpected)
|
||||
{
|
||||
auto dt = DateTime::Parse(str, DateTime::DateFormat::Rfc3339);
|
||||
auto const str2 = dt.GetRfc3339String(TF);
|
||||
auto const str2 = dt.ToString(DateTime::DateFormat::Rfc3339, TF);
|
||||
EXPECT_EQ(str2, strExpected);
|
||||
}
|
||||
|
||||
@ -85,21 +85,24 @@ TEST(DateTime, decimals)
|
||||
{
|
||||
std::string strExpected("2020-10-13T21:06:15.3300000Z");
|
||||
auto dt = DateTime::Parse("2020-10-13T21:06:15.33Z", DateTime::DateFormat::Rfc3339);
|
||||
auto const str2 = dt.GetRfc3339String(DateTime::TimeFractionFormat::AllDigits);
|
||||
auto const str2
|
||||
= dt.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits);
|
||||
EXPECT_EQ(str2, strExpected);
|
||||
}
|
||||
|
||||
{
|
||||
std::string strExpected("2020-10-13T21:06:15.0000000Z");
|
||||
auto dt = DateTime::Parse("2020-10-13T21:06:15Z", DateTime::DateFormat::Rfc3339);
|
||||
auto const str2 = dt.GetRfc3339String(DateTime::TimeFractionFormat::AllDigits);
|
||||
auto const str2
|
||||
= dt.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits);
|
||||
EXPECT_EQ(str2, strExpected);
|
||||
}
|
||||
|
||||
{
|
||||
std::string strExpected("2020-10-13T21:06:15.1234500Z");
|
||||
auto dt = DateTime::Parse("2020-10-13T21:06:15.12345Z", DateTime::DateFormat::Rfc3339);
|
||||
auto const str2 = dt.GetRfc3339String(DateTime::TimeFractionFormat::AllDigits);
|
||||
auto const str2
|
||||
= dt.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits);
|
||||
EXPECT_EQ(str2, strExpected);
|
||||
}
|
||||
}
|
||||
@ -109,14 +112,16 @@ TEST(DateTime, noDecimals)
|
||||
{
|
||||
std::string strExpected("2020-10-13T21:06:15Z");
|
||||
auto dt = DateTime::Parse("2020-10-13T21:06:15Z", DateTime::DateFormat::Rfc3339);
|
||||
auto const str2 = dt.GetRfc3339String(DateTime::TimeFractionFormat::Truncate);
|
||||
auto const str2
|
||||
= dt.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::Truncate);
|
||||
EXPECT_EQ(str2, strExpected);
|
||||
}
|
||||
|
||||
{
|
||||
std::string strExpected("2020-10-13T21:06:15Z");
|
||||
auto dt = DateTime::Parse("2020-10-13T21:06:15.99999Z", DateTime::DateFormat::Rfc3339);
|
||||
auto const str2 = dt.GetRfc3339String(DateTime::TimeFractionFormat::Truncate);
|
||||
auto const str2
|
||||
= dt.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::Truncate);
|
||||
EXPECT_EQ(str2, strExpected);
|
||||
}
|
||||
}
|
||||
@ -126,7 +131,8 @@ TEST(DateTime, sameResultFromDefaultRfc3339)
|
||||
{
|
||||
auto dt = DateTime::Parse("2020-10-13T21:06:15.33000000Z", DateTime::DateFormat::Rfc3339);
|
||||
auto dt2 = DateTime::Parse("2020-10-13T21:06:15.330000000Z", DateTime::DateFormat::Rfc3339);
|
||||
auto const str1 = dt.GetRfc3339String(DateTime::TimeFractionFormat::DropTrailingZeros);
|
||||
auto const str1 = dt.ToString(
|
||||
DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::DropTrailingZeros);
|
||||
auto const str2 = dt2.ToString(DateTime::DateFormat::Rfc3339);
|
||||
EXPECT_EQ(str1, str2);
|
||||
}
|
||||
|
||||
@ -2,225 +2,202 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <azure/core/internal/log.hpp>
|
||||
#include <azure/core/logging/logging.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace Azure::Core::Logging;
|
||||
using namespace Azure::Core::Logging::Internal;
|
||||
using Azure::Core::Logger;
|
||||
using Azure::Core::Internal::Log;
|
||||
|
||||
TEST(Logging, Defaults)
|
||||
TEST(Logger, Levels)
|
||||
{
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Error));
|
||||
Logger::SetListener([](auto, auto) {});
|
||||
|
||||
SetLogListener([](auto, auto) {});
|
||||
Logger::SetLevel(Logger::Level::Verbose);
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Verbose));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Informational));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Warning));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Error));
|
||||
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Error));
|
||||
Logger::SetLevel(Logger::Level::Informational);
|
||||
EXPECT_FALSE(Log::ShouldWrite(Logger::Level::Verbose));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Informational));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Warning));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Error));
|
||||
|
||||
SetLogListener(nullptr);
|
||||
Logger::SetLevel(Logger::Level::Warning);
|
||||
EXPECT_FALSE(Log::ShouldWrite(Logger::Level::Verbose));
|
||||
EXPECT_FALSE(Log::ShouldWrite(Logger::Level::Informational));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Warning));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Error));
|
||||
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Error));
|
||||
Logger::SetLevel(Logger::Level::Error);
|
||||
EXPECT_FALSE(Log::ShouldWrite(Logger::Level::Verbose));
|
||||
EXPECT_FALSE(Log::ShouldWrite(Logger::Level::Informational));
|
||||
EXPECT_FALSE(Log::ShouldWrite(Logger::Level::Warning));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Error));
|
||||
|
||||
Logger::SetLevel(Logger::Level::Verbose);
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Verbose));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Informational));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Warning));
|
||||
EXPECT_TRUE(Log::ShouldWrite(Logger::Level::Error));
|
||||
|
||||
Logger::SetListener(nullptr);
|
||||
}
|
||||
|
||||
TEST(Logging, Levels)
|
||||
{
|
||||
SetLogListener([](auto, auto) {});
|
||||
|
||||
SetLogLevel(LogLevel::Verbose);
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Error));
|
||||
|
||||
SetLogLevel(LogLevel::Informational);
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Error));
|
||||
|
||||
SetLogLevel(LogLevel::Warning);
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Error));
|
||||
|
||||
SetLogLevel(LogLevel::Error);
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_FALSE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Error));
|
||||
|
||||
SetLogLevel(LogLevel::Verbose);
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Verbose));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Informational));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Warning));
|
||||
EXPECT_TRUE(ShouldLog(LogLevel::Error));
|
||||
|
||||
SetLogListener(nullptr);
|
||||
}
|
||||
|
||||
TEST(Logging, Message)
|
||||
TEST(Logger, Message)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogLevel level = LogLevel::Error;
|
||||
Logger::Level level = Logger::Level::Error;
|
||||
std::string message;
|
||||
|
||||
SetLogListener([&](auto lvl, auto msg) {
|
||||
Logger::SetListener([&](auto lvl, auto msg) {
|
||||
level = lvl;
|
||||
message = msg;
|
||||
});
|
||||
|
||||
SetLogLevel(LogLevel::Verbose);
|
||||
Logger::SetLevel(Logger::Level::Verbose);
|
||||
{
|
||||
level = LogLevel::Error;
|
||||
level = Logger::Level::Error;
|
||||
message = "";
|
||||
|
||||
Log(LogLevel::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, LogLevel::Verbose);
|
||||
Log::Write(Logger::Level::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, Logger::Level::Verbose);
|
||||
EXPECT_EQ(message, "Verbose");
|
||||
|
||||
Log(LogLevel::Informational, "Informational");
|
||||
EXPECT_EQ(level, LogLevel::Informational);
|
||||
Log::Write(Logger::Level::Informational, "Informational");
|
||||
EXPECT_EQ(level, Logger::Level::Informational);
|
||||
EXPECT_EQ(message, "Informational");
|
||||
|
||||
Log(LogLevel::Warning, "Warning");
|
||||
EXPECT_EQ(level, LogLevel::Warning);
|
||||
Log::Write(Logger::Level::Warning, "Warning");
|
||||
EXPECT_EQ(level, Logger::Level::Warning);
|
||||
EXPECT_EQ(message, "Warning");
|
||||
|
||||
Log(LogLevel::Error, "Error");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Error, "Error");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "Error");
|
||||
}
|
||||
|
||||
SetLogLevel(LogLevel::Informational);
|
||||
Logger::SetLevel(Logger::Level::Informational);
|
||||
{
|
||||
level = LogLevel::Error;
|
||||
level = Logger::Level::Error;
|
||||
message = "";
|
||||
|
||||
Log(LogLevel::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
Log(LogLevel::Informational, "Informational");
|
||||
EXPECT_EQ(level, LogLevel::Informational);
|
||||
Log::Write(Logger::Level::Informational, "Informational");
|
||||
EXPECT_EQ(level, Logger::Level::Informational);
|
||||
EXPECT_EQ(message, "Informational");
|
||||
|
||||
Log(LogLevel::Warning, "Warning");
|
||||
EXPECT_EQ(level, LogLevel::Warning);
|
||||
Log::Write(Logger::Level::Warning, "Warning");
|
||||
EXPECT_EQ(level, Logger::Level::Warning);
|
||||
EXPECT_EQ(message, "Warning");
|
||||
|
||||
Log(LogLevel::Error, "Error");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Error, "Error");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "Error");
|
||||
}
|
||||
|
||||
SetLogLevel(LogLevel::Warning);
|
||||
Logger::SetLevel(Logger::Level::Warning);
|
||||
{
|
||||
level = LogLevel::Error;
|
||||
level = Logger::Level::Error;
|
||||
message = "";
|
||||
|
||||
Log(LogLevel::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
Log(LogLevel::Informational, "Informational");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Informational, "Informational");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
Log(LogLevel::Warning, "Warning");
|
||||
EXPECT_EQ(level, LogLevel::Warning);
|
||||
Log::Write(Logger::Level::Warning, "Warning");
|
||||
EXPECT_EQ(level, Logger::Level::Warning);
|
||||
EXPECT_EQ(message, "Warning");
|
||||
|
||||
Log(LogLevel::Error, "Error");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Error, "Error");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "Error");
|
||||
}
|
||||
|
||||
SetLogLevel(LogLevel::Error);
|
||||
Logger::SetLevel(Logger::Level::Error);
|
||||
{
|
||||
level = LogLevel::Error;
|
||||
level = Logger::Level::Error;
|
||||
message = "";
|
||||
|
||||
Log(LogLevel::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
Log(LogLevel::Informational, "Informational");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Informational, "Informational");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
Log(LogLevel::Warning, "Warning");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Warning, "Warning");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
level = LogLevel::Verbose;
|
||||
level = Logger::Level::Verbose;
|
||||
|
||||
Log(LogLevel::Error, "Error");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Error, "Error");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "Error");
|
||||
}
|
||||
|
||||
// Verify that we can switch back to Verbose
|
||||
SetLogLevel(LogLevel::Verbose);
|
||||
Logger::SetLevel(Logger::Level::Verbose);
|
||||
{
|
||||
level = LogLevel::Error;
|
||||
level = Logger::Level::Error;
|
||||
message = "";
|
||||
|
||||
Log(LogLevel::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, LogLevel::Verbose);
|
||||
Log::Write(Logger::Level::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, Logger::Level::Verbose);
|
||||
EXPECT_EQ(message, "Verbose");
|
||||
|
||||
Log(LogLevel::Informational, "Informational");
|
||||
EXPECT_EQ(level, LogLevel::Informational);
|
||||
Log::Write(Logger::Level::Informational, "Informational");
|
||||
EXPECT_EQ(level, Logger::Level::Informational);
|
||||
EXPECT_EQ(message, "Informational");
|
||||
|
||||
Log(LogLevel::Warning, "Warning");
|
||||
EXPECT_EQ(level, LogLevel::Warning);
|
||||
Log::Write(Logger::Level::Warning, "Warning");
|
||||
EXPECT_EQ(level, Logger::Level::Warning);
|
||||
EXPECT_EQ(message, "Warning");
|
||||
|
||||
Log(LogLevel::Error, "Error");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Error, "Error");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "Error");
|
||||
}
|
||||
|
||||
SetLogListener(nullptr);
|
||||
Logger::SetListener(nullptr);
|
||||
|
||||
SetLogLevel(LogLevel::Verbose);
|
||||
Logger::SetLevel(Logger::Level::Verbose);
|
||||
{
|
||||
level = LogLevel::Error;
|
||||
level = Logger::Level::Error;
|
||||
message = "";
|
||||
|
||||
Log(LogLevel::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Verbose, "Verbose");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
Log(LogLevel::Informational, "Informational");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Informational, "Informational");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
Log(LogLevel::Warning, "Warning");
|
||||
EXPECT_EQ(level, LogLevel::Error);
|
||||
Log::Write(Logger::Level::Warning, "Warning");
|
||||
EXPECT_EQ(level, Logger::Level::Error);
|
||||
EXPECT_EQ(message, "");
|
||||
|
||||
level = LogLevel::Verbose;
|
||||
level = Logger::Level::Verbose;
|
||||
|
||||
Log(LogLevel::Error, "Error");
|
||||
EXPECT_EQ(level, LogLevel::Verbose);
|
||||
Log::Write(Logger::Level::Error, "Error");
|
||||
EXPECT_EQ(level, Logger::Level::Verbose);
|
||||
EXPECT_EQ(message, "");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
SetLogListener(nullptr);
|
||||
Logger::SetListener(nullptr);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,8 @@ class DllExportTest {
|
||||
|
||||
TEST(SimplifiedHeader, core)
|
||||
{
|
||||
EXPECT_NO_THROW(Azure::Core::CaseInsensitiveMap imap);
|
||||
EXPECT_NO_THROW(Azure::Core::CaseInsensitiveSet iset);
|
||||
EXPECT_NO_THROW(Azure::Core::Context c);
|
||||
EXPECT_NO_THROW(Azure::Core::DateTime(2020, 11, 03, 15, 30, 44));
|
||||
EXPECT_NO_THROW(Azure::Core::ETag e);
|
||||
@ -35,6 +37,7 @@ TEST(SimplifiedHeader, core)
|
||||
EXPECT_NO_THROW(Azure::Core::Cryptography::Md5Hash m);
|
||||
EXPECT_NO_THROW(Azure::Core::Http::RawResponse r(
|
||||
1, 1, Azure::Core::Http::HttpStatusCode::Accepted, "phrase"));
|
||||
EXPECT_NO_THROW(Azure::Core::Logger::Listener ll = nullptr);
|
||||
EXPECT_NO_THROW(Azure::Core::MatchConditions mc);
|
||||
EXPECT_NO_THROW(Azure::Core::ModifiedConditions mc);
|
||||
EXPECT_NO_THROW(Azure::Core::Nullable<int> n);
|
||||
|
||||
@ -2856,7 +2856,10 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
writer.Write(Storage::Details::XmlNode{
|
||||
Storage::Details::XmlNodeType::Text,
|
||||
nullptr,
|
||||
options.StartsOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
options.StartsOn
|
||||
.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
.data()});
|
||||
writer.Write(Storage::Details::XmlNode{Storage::Details::XmlNodeType::EndTag});
|
||||
writer.Write(
|
||||
@ -2865,7 +2868,9 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
Storage::Details::XmlNodeType::Text,
|
||||
nullptr,
|
||||
options.ExpiresOn
|
||||
.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
.data()});
|
||||
writer.Write(Storage::Details::XmlNode{Storage::Details::XmlNodeType::EndTag});
|
||||
writer.Write(Storage::Details::XmlNode{Storage::Details::XmlNodeType::EndTag});
|
||||
@ -4805,7 +4810,9 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
Storage::Details::XmlNodeType::Text,
|
||||
nullptr,
|
||||
options.StartsOn
|
||||
.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
.data()});
|
||||
writer.Write(Storage::Details::XmlNode{Storage::Details::XmlNodeType::EndTag});
|
||||
writer.Write(
|
||||
@ -4814,7 +4821,9 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
Storage::Details::XmlNodeType::Text,
|
||||
nullptr,
|
||||
options.ExpiresOn
|
||||
.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
.data()});
|
||||
writer.Write(Storage::Details::XmlNode{Storage::Details::XmlNodeType::EndTag});
|
||||
writer.Write(
|
||||
|
||||
@ -128,12 +128,14 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
snapshotVersion = BlobVersionId;
|
||||
}
|
||||
|
||||
std::string startsOnStr = StartsOn.HasValue()
|
||||
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = Identifier.empty()
|
||||
? ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string startsOnStr = StartsOn.HasValue() ? StartsOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = Identifier.empty() ? ExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
|
||||
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
+ canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "")
|
||||
@ -222,14 +224,18 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
snapshotVersion = BlobVersionId;
|
||||
}
|
||||
|
||||
std::string startsOnStr = StartsOn.HasValue()
|
||||
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr
|
||||
= ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
std::string signedStartsOnStr = userDelegationKey.SignedStartsOn.GetRfc3339String(
|
||||
std::string startsOnStr = StartsOn.HasValue() ? StartsOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = ExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
std::string signedExpiresOnStr = userDelegationKey.SignedExpiresOn.GetRfc3339String(
|
||||
std::string signedStartsOnStr = userDelegationKey.SignedStartsOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
std::string signedExpiresOnStr = userDelegationKey.SignedExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
|
||||
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <azure/core/case_insensitive_map.hpp>
|
||||
#include <azure/core/case_insensitive_containers.hpp>
|
||||
#include <azure/core/http/policy.hpp>
|
||||
|
||||
#include "azure/storage/common/constants.hpp"
|
||||
|
||||
@ -91,11 +91,13 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
resourceTypes += "o";
|
||||
}
|
||||
|
||||
std::string startsOnStr = StartsOn.HasValue()
|
||||
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr
|
||||
= ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
std::string startsOnStr = StartsOn.HasValue() ? StartsOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = ExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
|
||||
std::string stringToSign = credential.AccountName + "\n" + Permissions + "\n" + services + "\n"
|
||||
+ resourceTypes + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
|
||||
@ -123,12 +123,14 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
std::string protocol = Details::SasProtocolToString(Protocol);
|
||||
std::string resource = DataLakeSasResourceToString(Resource);
|
||||
|
||||
std::string startsOnStr = StartsOn.HasValue()
|
||||
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = Identifier.empty()
|
||||
? ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string startsOnStr = StartsOn.HasValue() ? StartsOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = Identifier.empty() ? ExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
|
||||
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
+ canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "")
|
||||
@ -206,14 +208,18 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
std::string protocol = Details::SasProtocolToString(Protocol);
|
||||
std::string resource = DataLakeSasResourceToString(Resource);
|
||||
|
||||
std::string startsOnStr = StartsOn.HasValue()
|
||||
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr
|
||||
= ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
std::string signedStartsOnStr = userDelegationKey.SignedStartsOn.GetRfc3339String(
|
||||
std::string startsOnStr = StartsOn.HasValue() ? StartsOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = ExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
std::string signedExpiresOnStr = userDelegationKey.SignedExpiresOn.GetRfc3339String(
|
||||
std::string signedStartsOnStr = userDelegationKey.SignedStartsOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
std::string signedExpiresOnStr = userDelegationKey.SignedExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate);
|
||||
|
||||
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
|
||||
@ -3869,7 +3869,10 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
writer.Write(Storage::Details::XmlNode{
|
||||
Storage::Details::XmlNodeType::Text,
|
||||
nullptr,
|
||||
object.StartsOn.GetRfc3339String(Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
object.StartsOn
|
||||
.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
.data()});
|
||||
writer.Write(Storage::Details::XmlNode{Storage::Details::XmlNodeType::EndTag});
|
||||
writer.Write(
|
||||
@ -3877,7 +3880,10 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
writer.Write(Storage::Details::XmlNode{
|
||||
Storage::Details::XmlNodeType::Text,
|
||||
nullptr,
|
||||
object.ExpiresOn.GetRfc3339String(Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
object.ExpiresOn
|
||||
.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits)
|
||||
.data()});
|
||||
writer.Write(Storage::Details::XmlNode{Storage::Details::XmlNodeType::EndTag});
|
||||
writer.Write(
|
||||
|
||||
@ -117,9 +117,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
}
|
||||
if (options.SmbProperties.CreatedOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCreationTime
|
||||
= options.SmbProperties.CreatedOn.GetValue().GetRfc3339String(
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
protocolLayerOptions.FileCreationTime = options.SmbProperties.CreatedOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -128,7 +128,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
if (options.SmbProperties.LastWrittenOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileLastWriteTime
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().GetRfc3339String(
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
@ -242,7 +243,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
protocolLayerOptions.FileAttributes = smbProperties.Attributes.ToString();
|
||||
if (smbProperties.CreatedOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCreationTime = smbProperties.CreatedOn.GetValue().GetRfc3339String(
|
||||
protocolLayerOptions.FileCreationTime = smbProperties.CreatedOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
@ -251,9 +253,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
}
|
||||
if (smbProperties.LastWrittenOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileLastWriteTime
|
||||
= smbProperties.LastWrittenOn.GetValue().GetRfc3339String(
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
protocolLayerOptions.FileLastWriteTime = smbProperties.LastWrittenOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -106,9 +106,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
}
|
||||
if (options.SmbProperties.CreatedOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCreationTime
|
||||
= options.SmbProperties.CreatedOn.GetValue().GetRfc3339String(
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
protocolLayerOptions.FileCreationTime = options.SmbProperties.CreatedOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -117,7 +117,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
if (options.SmbProperties.LastWrittenOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileLastWriteTime
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().GetRfc3339String(
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
@ -322,7 +323,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
if (options.SmbProperties.CreatedOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCopyFileCreationTime
|
||||
= options.SmbProperties.CreatedOn.GetValue().GetRfc3339String(
|
||||
= options.SmbProperties.CreatedOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
@ -332,7 +334,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
if (options.SmbProperties.LastWrittenOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCopyFileLastWriteTime
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().GetRfc3339String(
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
@ -416,7 +419,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
}
|
||||
if (smbProperties.CreatedOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCreationTime = smbProperties.CreatedOn.GetValue().GetRfc3339String(
|
||||
protocolLayerOptions.FileCreationTime = smbProperties.CreatedOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
@ -425,9 +429,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
}
|
||||
if (smbProperties.LastWrittenOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileLastWriteTime
|
||||
= smbProperties.LastWrittenOn.GetValue().GetRfc3339String(
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
protocolLayerOptions.FileLastWriteTime = smbProperties.LastWrittenOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -843,9 +847,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
}
|
||||
if (options.SmbProperties.CreatedOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCreationTime
|
||||
= options.SmbProperties.CreatedOn.GetValue().GetRfc3339String(
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
protocolLayerOptions.FileCreationTime = options.SmbProperties.CreatedOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -854,7 +858,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
if (options.SmbProperties.LastWrittenOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileLastWriteTime
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().GetRfc3339String(
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
@ -948,9 +953,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
}
|
||||
if (options.SmbProperties.CreatedOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileCreationTime
|
||||
= options.SmbProperties.CreatedOn.GetValue().GetRfc3339String(
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
protocolLayerOptions.FileCreationTime = options.SmbProperties.CreatedOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -959,7 +964,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
if (options.SmbProperties.LastWrittenOn.HasValue())
|
||||
{
|
||||
protocolLayerOptions.FileLastWriteTime
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().GetRfc3339String(
|
||||
= options.SmbProperties.LastWrittenOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Core::DateTime::TimeFractionFormat::AllDigits);
|
||||
}
|
||||
else
|
||||
|
||||
@ -84,12 +84,14 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
std::string protocol = Details::SasProtocolToString(Protocol);
|
||||
std::string resource = ShareSasResourceToString(Resource);
|
||||
|
||||
std::string startsOnStr = StartsOn.HasValue()
|
||||
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = Identifier.empty()
|
||||
? ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string startsOnStr = StartsOn.HasValue() ? StartsOn.GetValue().ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
std::string expiresOnStr = Identifier.empty() ? ExpiresOn.ToString(
|
||||
Azure::Core::DateTime::DateFormat::Rfc3339,
|
||||
Azure::Core::DateTime::TimeFractionFormat::Truncate)
|
||||
: "";
|
||||
|
||||
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
+ canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user