* Fixes #4696 - Added support to Log::Write to use insertion operators for logging * EnvironmentLogger writes to stderr on errors, stdout for non-errors; Also don't double insert crlf in messages --------- Co-authored-by: Ahson Khan <ahkha@microsoft.com>
This commit is contained in:
parent
2f7a3eec8b
commit
0dd8882462
@ -120,9 +120,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
auto cbs = static_cast<ClaimsBasedSecurityImpl*>(const_cast<void*>(context));
|
||||
if (cbs->m_traceEnabled)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "OnCbsOpenComplete: " << OpenResultStringFromLowLevel(openCompleteResult);
|
||||
Log::Write(Logger::Level::Informational, ss.str());
|
||||
Log::Stream(Logger::Level::Informational)
|
||||
<< "OnCbsOpenComplete: " << OpenResultStringFromLowLevel(openCompleteResult) << std::endl;
|
||||
}
|
||||
cbs->m_openResultQueue.CompleteOperation(CbsOpenResultStateFromLowLevel(openCompleteResult));
|
||||
}
|
||||
@ -158,11 +157,11 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
auto cbs = static_cast<ClaimsBasedSecurityImpl*>(const_cast<void*>(context));
|
||||
if (cbs->m_traceEnabled)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "OnCbsOperationComplete: " << OperationResultStringFromLowLevel(operationCompleteResult)
|
||||
<< " StatusCode: " << statusCode << " StatusDescription: "
|
||||
<< (statusDescription ? std::string(statusDescription) : "(NULL)");
|
||||
Log::Write(Logger::Level::Informational, ss.str());
|
||||
Log::Stream(Logger::Level::Informational)
|
||||
<< "OnCbsOperationComplete: "
|
||||
<< OperationResultStringFromLowLevel(operationCompleteResult)
|
||||
<< " StatusCode: " << statusCode << " StatusDescription: "
|
||||
<< (statusDescription ? std::string(statusDescription) : "(NULL)") << std::endl;
|
||||
}
|
||||
|
||||
cbs->m_operationResultQueue.CompleteOperation(
|
||||
|
||||
@ -183,8 +183,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
ManagementClientImpl* management = static_cast<ManagementClientImpl*>(context);
|
||||
if (management->m_options.EnableTrace)
|
||||
{
|
||||
Log::Write(
|
||||
Logger::Level::Informational, "OnManagementOpenComplete: " + std::to_string(openResult));
|
||||
Log::Stream(Logger::Level::Informational)
|
||||
<< "OnManagementOpenComplete: " << std::to_string(openResult) << std::endl;
|
||||
}
|
||||
management->m_openCompleteQueue.CompleteOperation(openResult);
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-Licence-Identifier: MIT
|
||||
|
||||
// Enable declaration of strerror_s.
|
||||
#define __STDC_WANT_LIB_EXT1__ 1
|
||||
|
||||
#include "azure/core/amqp/message_receiver.hpp"
|
||||
|
||||
#include "azure/core/amqp/connection.hpp"
|
||||
@ -14,6 +17,7 @@
|
||||
#include <azure/core/credentials/credentials.hpp>
|
||||
#include <azure/core/diagnostics/logger.hpp>
|
||||
#include <azure/core/internal/diagnostics/log.hpp>
|
||||
#include <azure/core/platform.hpp>
|
||||
|
||||
#include <azure_uamqp_c/message_receiver.h>
|
||||
|
||||
@ -103,10 +107,9 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
m_eventHandler->OnMessageReceiverDisconnected(error);
|
||||
}
|
||||
// Log that an error occurred.
|
||||
Log::Write(
|
||||
Logger::Level::Error,
|
||||
"Message receiver link detached: " + error.Condition.ToString() + ": "
|
||||
+ error.Description);
|
||||
Log::Stream(Logger::Level::Error)
|
||||
<< "Message receiver link detached: " + error.Condition.ToString() << ": "
|
||||
<< error.Description << std::endl;
|
||||
|
||||
// Cache the error we received in the OnDetach notification so we can return it to the user on
|
||||
// the next send which fails.
|
||||
@ -232,10 +235,10 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Message receiver changed state. New: " << MESSAGE_RECEIVER_STATEStrings[newState]
|
||||
<< " Old: " << MESSAGE_RECEIVER_STATEStrings[oldState] << std::endl;
|
||||
Log::Write(Logger::Level::Verbose, ss.str());
|
||||
Log::Stream(Logger::Level::Verbose)
|
||||
<< "Message receiver changed state. New: " << MESSAGE_RECEIVER_STATEStrings[newState]
|
||||
<< " Old: " << MESSAGE_RECEIVER_STATEStrings[oldState] << std::endl;
|
||||
;
|
||||
}
|
||||
|
||||
// If we are transitioning to the error state, we want to stick a response on the incoming queue
|
||||
@ -279,38 +282,34 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
auto err = errno;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
// warning C4996: 'strerror': This function or variable may be unsafe. Consider using gmtime_s
|
||||
// instead.
|
||||
#pragma warning(disable : 4996)
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
char buf[256];
|
||||
strerror_s(buf, sizeof(buf), err);
|
||||
#else
|
||||
std::string buf{strerror(err)};
|
||||
#endif
|
||||
throw std::runtime_error(
|
||||
"Could not open message receiver. errno=" + std::to_string(err) + ", \""
|
||||
+ strerror(err) + "\".");
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(
|
||||
"Could not open message receiver. errno=" + std::to_string(err) + ", \"" + buf + "\".");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
void MessageReceiverImpl::Close()
|
||||
{
|
||||
if (messagereceiver_close(m_messageReceiver.get()))
|
||||
{
|
||||
throw std::runtime_error("Could not close message receiver"); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
void MessageReceiverImpl::Close()
|
||||
{
|
||||
if (messagereceiver_close(m_messageReceiver.get()))
|
||||
{
|
||||
throw std::runtime_error("Could not close message receiver"); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
std::string MessageReceiverImpl::GetLinkName() const
|
||||
{
|
||||
const char* linkName;
|
||||
if (messagereceiver_get_link_name(m_messageReceiver.get(), &linkName))
|
||||
{
|
||||
throw std::runtime_error("Could not get link name");
|
||||
}
|
||||
return std::string(linkName);
|
||||
}
|
||||
std::string MessageReceiverImpl::GetLinkName() const
|
||||
{
|
||||
const char* linkName;
|
||||
if (messagereceiver_get_link_name(m_messageReceiver.get(), &linkName))
|
||||
{
|
||||
throw std::runtime_error("Could not get link name");
|
||||
}
|
||||
return std::string(linkName);
|
||||
}
|
||||
|
||||
}}}} // namespace Azure::Core::Amqp::_detail
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-Licence-Identifier: MIT
|
||||
|
||||
// Enable declaration of strerror_s.
|
||||
#define __STDC_WANT_LIB_EXT1__ 1
|
||||
|
||||
#include "azure/core/amqp/claims_based_security.hpp"
|
||||
#include "azure/core/amqp/common/completion_operation.hpp"
|
||||
#include "azure/core/amqp/models/amqp_message.hpp"
|
||||
@ -13,6 +16,7 @@
|
||||
#include <azure/core/credentials/credentials.hpp>
|
||||
#include <azure/core/diagnostics/logger.hpp>
|
||||
#include <azure/core/internal/diagnostics/log.hpp>
|
||||
#include <azure/core/platform.hpp>
|
||||
|
||||
#include <azure_uamqp_c/message_sender.h>
|
||||
|
||||
@ -117,9 +121,9 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
m_events->OnMessageSenderDisconnected(error);
|
||||
}
|
||||
// Log that an error occurred.
|
||||
Log::Write(
|
||||
Logger::Level::Error,
|
||||
"Message sender link detached: " + error.Condition.ToString() + ": " + error.Description);
|
||||
Log::Stream(Logger::Level::Error)
|
||||
<< "Message sender link detached: " << error.Condition.ToString() << ": "
|
||||
<< error.Description << std::endl;
|
||||
|
||||
// Cache the error we received in the OnDetach notification so we can return it to the user on
|
||||
// the next send which fails.
|
||||
@ -202,118 +206,114 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
auto err = errno;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
// warning C4996: 'strerror': This function or variable may be unsafe. Consider using strerror_s
|
||||
// instead.
|
||||
#pragma warning(disable : 4996)
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
char buf[256];
|
||||
strerror_s(buf, sizeof(buf), err);
|
||||
#else
|
||||
std::string buf{strerror(err)};
|
||||
#endif
|
||||
throw std::runtime_error(
|
||||
"Could not open message sender. errno=" + std::to_string(err) + ", \"" + strerror(err)
|
||||
+ "\".");
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
void MessageSenderImpl::Close()
|
||||
{
|
||||
if (messagesender_close(m_messageSender.get()))
|
||||
{
|
||||
throw std::runtime_error("Could not close message sender"); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(
|
||||
"Could not open message sender. errno=" + std::to_string(err) + ", \"" + buf + "\".");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
void MessageSenderImpl::Close()
|
||||
{
|
||||
if (messagesender_close(m_messageSender.get()))
|
||||
{
|
||||
throw std::runtime_error("Could not close message sender"); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CompleteFn> struct RewriteSendComplete
|
||||
template <typename CompleteFn> struct RewriteSendComplete
|
||||
{
|
||||
static void OnOperation(
|
||||
CompleteFn onComplete,
|
||||
MESSAGE_SEND_RESULT sendResult,
|
||||
AMQP_VALUE disposition)
|
||||
{
|
||||
_internal::MessageSendStatus result{_internal::MessageSendStatus::Ok};
|
||||
switch (sendResult)
|
||||
{
|
||||
static void OnOperation(
|
||||
CompleteFn onComplete,
|
||||
MESSAGE_SEND_RESULT sendResult,
|
||||
AMQP_VALUE disposition)
|
||||
{
|
||||
_internal::MessageSendStatus result{_internal::MessageSendStatus::Ok};
|
||||
switch (sendResult)
|
||||
case MESSAGE_SEND_RESULT_INVALID: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Invalid; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
case MESSAGE_SEND_OK:
|
||||
result = _internal::MessageSendStatus::Ok;
|
||||
break;
|
||||
case MESSAGE_SEND_CANCELLED: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Cancelled; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
case MESSAGE_SEND_ERROR: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Error; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
case MESSAGE_SEND_TIMEOUT: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Timeout; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
}
|
||||
onComplete(result, disposition);
|
||||
}
|
||||
};
|
||||
|
||||
void MessageSenderImpl::QueueSend(
|
||||
Models::AmqpMessage const& message,
|
||||
Azure::Core::Amqp::_internal::MessageSender::MessageSendCompleteCallback onSendComplete,
|
||||
Context const& context)
|
||||
{
|
||||
auto operation(std::make_unique<Azure::Core::Amqp::Common::_internal::CompletionOperation<
|
||||
decltype(onSendComplete),
|
||||
RewriteSendComplete<decltype(onSendComplete)>>>(onSendComplete));
|
||||
auto result = messagesender_send_async(
|
||||
m_messageSender.get(),
|
||||
Models::_internal::AmqpMessageFactory::ToUamqp(message).get(),
|
||||
std::remove_pointer<decltype(operation)::element_type>::type::OnOperationFn,
|
||||
operation.release(),
|
||||
0 /*timeout*/);
|
||||
if (result == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Could not send message"); // LCOV_EXCL_LINE
|
||||
}
|
||||
(void)context;
|
||||
}
|
||||
|
||||
std::tuple<_internal::MessageSendStatus, Models::AmqpValue> MessageSenderImpl::Send(
|
||||
Models::AmqpMessage const& message,
|
||||
Context const& context)
|
||||
{
|
||||
Azure::Core::Amqp::Common::_internal::
|
||||
AsyncOperationQueue<Azure::Core::Amqp::_internal::MessageSendStatus, Models::AmqpValue>
|
||||
sendCompleteQueue;
|
||||
|
||||
QueueSend(
|
||||
message,
|
||||
[&sendCompleteQueue, this](
|
||||
Azure::Core::Amqp::_internal::MessageSendStatus sendResult,
|
||||
Models::AmqpValue deliveryStatus) {
|
||||
// If the send failed. then we need to return the error. If the send completed because
|
||||
// of an error, it's possible that the deliveryStatus provided is null. In that case,
|
||||
// we use the cached saved error because it is highly likely to be better than
|
||||
// nothing.
|
||||
if (sendResult != _internal::MessageSendStatus::Ok)
|
||||
{
|
||||
case MESSAGE_SEND_RESULT_INVALID: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Invalid; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
case MESSAGE_SEND_OK:
|
||||
result = _internal::MessageSendStatus::Ok;
|
||||
break;
|
||||
case MESSAGE_SEND_CANCELLED: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Cancelled; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
case MESSAGE_SEND_ERROR: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Error; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
case MESSAGE_SEND_TIMEOUT: // LCOV_EXCL_LINE
|
||||
result = _internal::MessageSendStatus::Timeout; // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
if (deliveryStatus.IsNull())
|
||||
{
|
||||
deliveryStatus = m_savedMessageError;
|
||||
}
|
||||
}
|
||||
onComplete(result, disposition);
|
||||
}
|
||||
};
|
||||
|
||||
void MessageSenderImpl::QueueSend(
|
||||
Models::AmqpMessage const& message,
|
||||
Azure::Core::Amqp::_internal::MessageSender::MessageSendCompleteCallback onSendComplete,
|
||||
Context const& context)
|
||||
{
|
||||
auto operation(std::make_unique<Azure::Core::Amqp::Common::_internal::CompletionOperation<
|
||||
decltype(onSendComplete),
|
||||
RewriteSendComplete<decltype(onSendComplete)>>>(onSendComplete));
|
||||
auto result = messagesender_send_async(
|
||||
m_messageSender.get(),
|
||||
Models::_internal::AmqpMessageFactory::ToUamqp(message).get(),
|
||||
std::remove_pointer<decltype(operation)::element_type>::type::OnOperationFn,
|
||||
operation.release(),
|
||||
0 /*timeout*/);
|
||||
if (result == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Could not send message"); // LCOV_EXCL_LINE
|
||||
}
|
||||
(void)context;
|
||||
}
|
||||
|
||||
std::tuple<_internal::MessageSendStatus, Models::AmqpValue> MessageSenderImpl::Send(
|
||||
Models::AmqpMessage const& message,
|
||||
Context const& context)
|
||||
{
|
||||
Azure::Core::Amqp::Common::_internal::
|
||||
AsyncOperationQueue<Azure::Core::Amqp::_internal::MessageSendStatus, Models::AmqpValue>
|
||||
sendCompleteQueue;
|
||||
|
||||
QueueSend(
|
||||
message,
|
||||
[&sendCompleteQueue, this](
|
||||
Azure::Core::Amqp::_internal::MessageSendStatus sendResult,
|
||||
Models::AmqpValue deliveryStatus) {
|
||||
// If the send failed. then we need to return the error. If the send completed because
|
||||
// of an error, it's possible that the deliveryStatus provided is null. In that case,
|
||||
// we use the cached saved error because it is highly likely to be better than
|
||||
// nothing.
|
||||
if (sendResult != _internal::MessageSendStatus::Ok)
|
||||
{
|
||||
if (deliveryStatus.IsNull())
|
||||
{
|
||||
deliveryStatus = m_savedMessageError;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we successfully sent the message, then whatever saved error should be cleared,
|
||||
// it's no longer valid.
|
||||
m_savedMessageError = Models::AmqpValue();
|
||||
}
|
||||
sendCompleteQueue.CompleteOperation(sendResult, deliveryStatus);
|
||||
},
|
||||
context);
|
||||
auto result = sendCompleteQueue.WaitForPolledResult(context, *m_session->GetConnection());
|
||||
if (result)
|
||||
{
|
||||
return std::move(*result);
|
||||
}
|
||||
throw std::runtime_error("Error sending message"); // LCOV_EXCL_LINE
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we successfully sent the message, then whatever saved error should be cleared,
|
||||
// it's no longer valid.
|
||||
m_savedMessageError = Models::AmqpValue();
|
||||
}
|
||||
sendCompleteQueue.CompleteOperation(sendResult, deliveryStatus);
|
||||
},
|
||||
context);
|
||||
auto result = sendCompleteQueue.WaitForPolledResult(context, *m_session->GetConnection());
|
||||
if (result)
|
||||
{
|
||||
return std::move(*result);
|
||||
}
|
||||
throw std::runtime_error("Error sending message"); // LCOV_EXCL_LINE
|
||||
}
|
||||
}}}} // namespace Azure::Core::Amqp::_detail
|
||||
|
||||
@ -237,9 +237,9 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail {
|
||||
m_claimsBasedSecurity = std::make_shared<ClaimsBasedSecurityImpl>(shared_from_this());
|
||||
}
|
||||
auto accessToken = GetConnection()->GetSecurityToken(audience, context);
|
||||
Log::Write(
|
||||
Logger::Level::Informational,
|
||||
"Authenticate with audience: " + audience + ", token: " + accessToken);
|
||||
Log::Stream(Logger::Level::Informational)
|
||||
<< "Authenticate with audience: " << audience << ", token: " << accessToken << std::endl;
|
||||
|
||||
m_claimsBasedSecurity->SetTrace(GetConnection()->EnableTrace());
|
||||
if (!m_cbsOpen)
|
||||
{
|
||||
|
||||
@ -24,9 +24,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace Network { namespac
|
||||
uint16_t port,
|
||||
TransportEvents* eventHandler)
|
||||
{
|
||||
Log::Write(
|
||||
Logger::Level::Verbose,
|
||||
"Create socket transport for host " + host + " port: " + std::to_string(port));
|
||||
Log::Stream(Logger::Level::Verbose)
|
||||
<< "Create socket transport for host " << host << " port: " << port << std::endl;
|
||||
|
||||
SOCKETIO_CONFIG socketConfig{host.c_str(), port, nullptr};
|
||||
return _detail::TransportImpl::CreateFromXioHandle(
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
### Other Changes
|
||||
|
||||
- The default logger now logs all non-error outputs to `stdout` instead of `stderr`. Log level `Error` is still logged to `stderr`.
|
||||
|
||||
## 1.10.0 (2023-06-01)
|
||||
|
||||
### Features Added
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
#include "azure/core/dll_import_export.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Azure { namespace Core { namespace Diagnostics { namespace _internal {
|
||||
@ -25,7 +27,29 @@ namespace Azure { namespace Core { namespace Diagnostics { namespace _internal {
|
||||
Log() = delete;
|
||||
~Log() = delete;
|
||||
|
||||
class LoggerStringBuffer : public std::stringbuf {
|
||||
public:
|
||||
LoggerStringBuffer(Logger::Level level) : m_level{level} {}
|
||||
LoggerStringBuffer(LoggerStringBuffer&& that) = default;
|
||||
LoggerStringBuffer& operator=(LoggerStringBuffer&& that) = default;
|
||||
~LoggerStringBuffer() override = default;
|
||||
|
||||
virtual int sync() override;
|
||||
|
||||
private:
|
||||
Logger::Level m_level;
|
||||
};
|
||||
|
||||
public:
|
||||
class LoggerStream : public std::basic_ostream<char> {
|
||||
public:
|
||||
LoggerStream(Logger::Level level) : std::ostream(&m_stringBuffer), m_stringBuffer{level} {}
|
||||
~LoggerStream() override = default;
|
||||
|
||||
private:
|
||||
LoggerStringBuffer m_stringBuffer;
|
||||
};
|
||||
|
||||
static bool ShouldWrite(Logger::Level level)
|
||||
{
|
||||
return g_isLoggingEnabled && level >= g_logLevel;
|
||||
@ -35,5 +59,6 @@ namespace Azure { namespace Core { namespace Diagnostics { namespace _internal {
|
||||
|
||||
static void EnableLogging(bool isEnabled);
|
||||
static void SetLogLevel(Logger::Level logLevel);
|
||||
static LoggerStream& Stream(Logger::Level level);
|
||||
};
|
||||
}}}} // namespace Azure::Core::Diagnostics::_internal
|
||||
|
||||
@ -120,11 +120,30 @@ EnvironmentLogLevelListener::GetLogListener()
|
||||
|
||||
static std::function<void(Logger::Level level, std::string const& message)> const consoleLogger =
|
||||
[](auto level, auto message) {
|
||||
std::cerr << '['
|
||||
<< Azure::DateTime(std::chrono::system_clock::now())
|
||||
.ToString(
|
||||
DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits)
|
||||
<< "] " << LogLevelToConsoleString(level) << " : " << message << std::endl;
|
||||
std::ostream* os = &std::cout;
|
||||
if (level == Logger::Level::Error)
|
||||
{
|
||||
os = &std::cerr;
|
||||
}
|
||||
*os << '['
|
||||
<< Azure::DateTime(std::chrono::system_clock::now())
|
||||
.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits)
|
||||
<< "] " << LogLevelToConsoleString(level) << " : " << message;
|
||||
|
||||
// If the message ends with a new line, flush the stream otherwise insert a new line to
|
||||
// terminate the message.
|
||||
//
|
||||
// If the client of the logger APIs is using the stream form of the logger, then it will
|
||||
// insert a \n character when the client uses std::endl. This check ensures that we don't
|
||||
// insert unnecessary new lines.
|
||||
if (!message.empty() && message.back() == '\n')
|
||||
{
|
||||
*os << std::flush;
|
||||
}
|
||||
else
|
||||
{
|
||||
*os << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
return consoleLogger;
|
||||
|
||||
@ -1487,7 +1487,7 @@ namespace Azure { namespace Core {
|
||||
|
||||
Azure::Core::_internal::UniqueHandle<X509_CRL> LoadCrlFromUrl(std::string const& url)
|
||||
{
|
||||
Log::Write(Logger::Level::Informational, "Load CRL from Url: " + url);
|
||||
Log::Stream(Logger::Level::Informational) << "Load CRL from Url: " << url << std::endl;
|
||||
Azure::Core::_internal::UniqueHandle<X509_CRL> crl;
|
||||
#if defined(USE_OPENSSL_3)
|
||||
crl = Azure::Core::_internal::MakeUniqueHandle(
|
||||
|
||||
@ -6,8 +6,10 @@
|
||||
#include "azure/core/internal/diagnostics/log.hpp"
|
||||
#include "private/environment_log_level_listener.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <sstream>
|
||||
|
||||
using namespace Azure::Core::Diagnostics;
|
||||
using namespace Azure::Core::Diagnostics::_internal;
|
||||
@ -49,3 +51,35 @@ void Logger::SetListener(
|
||||
}
|
||||
|
||||
void Logger::SetLevel(Logger::Level level) { Log::SetLogLevel(level); }
|
||||
|
||||
int Log::LoggerStringBuffer::sync()
|
||||
{
|
||||
Log::Write(m_level, str());
|
||||
str(std::string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Log::LoggerStream g_verboseLogger{Logger::Level::Verbose};
|
||||
static Log::LoggerStream g_informationalLogger{Logger::Level::Informational};
|
||||
static Log::LoggerStream g_warningLogger{Logger::Level::Warning};
|
||||
static Log::LoggerStream g_errorLogger{Logger::Level::Error};
|
||||
|
||||
/** Returns a custom ostream implementation with a logger based stream buffer.
|
||||
* @param level The level of the log message.
|
||||
* @return A custom ostream implementation.
|
||||
*/
|
||||
Log::LoggerStream& Log::Stream(Logger::Level level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Logger::Level::Verbose:
|
||||
return g_verboseLogger;
|
||||
case Logger::Level::Informational:
|
||||
return g_informationalLogger;
|
||||
case Logger::Level::Warning:
|
||||
return g_warningLogger;
|
||||
case Logger::Level::Error:
|
||||
return g_errorLogger;
|
||||
}
|
||||
throw std::runtime_error("Unknown stream logger level.");
|
||||
}
|
||||
|
||||
@ -127,16 +127,15 @@ TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerVerbose)
|
||||
SetLogLevel("verbose");
|
||||
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cerr.rdbuf(buffer.rdbuf());
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
|
||||
std::string text = buffer.str(); // text will now contain "Bla\n"
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
|
||||
listener(Logger::Level::Verbose, "message");
|
||||
EXPECT_NE(buffer.str().find("DEBUG : message"), std::string::npos);
|
||||
std::cerr.rdbuf(old);
|
||||
std::cout.rdbuf(old);
|
||||
}
|
||||
|
||||
TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerError)
|
||||
@ -144,10 +143,10 @@ TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerError)
|
||||
EnvironmentLogLevelListener::SetInitialized(false);
|
||||
SetLogLevel("verbose");
|
||||
|
||||
// Error logging goes to cerr, all other logging goes to cout.
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cerr.rdbuf(buffer.rdbuf());
|
||||
|
||||
std::string text = buffer.str(); // text will now contain "Bla\n"
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
@ -162,17 +161,17 @@ TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerWarning)
|
||||
EnvironmentLogLevelListener::SetInitialized(false);
|
||||
SetLogLevel("verbose");
|
||||
|
||||
// Error logging goes to cerr, all other logging goes to cout.
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cerr.rdbuf(buffer.rdbuf());
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
|
||||
std::string text = buffer.str(); // text will now contain "Bla\n"
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
|
||||
listener(Logger::Level::Warning, "message");
|
||||
EXPECT_NE(buffer.str().find("WARN : message"), std::string::npos);
|
||||
std::cerr.rdbuf(old);
|
||||
std::cout.rdbuf(old);
|
||||
}
|
||||
|
||||
TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerInformational)
|
||||
@ -180,17 +179,17 @@ TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerInformational)
|
||||
EnvironmentLogLevelListener::SetInitialized(false);
|
||||
SetLogLevel("verbose");
|
||||
|
||||
// Error logging goes to cerr, all other logging goes to cout.
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cerr.rdbuf(buffer.rdbuf());
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
|
||||
std::string text = buffer.str(); // text will now contain "Bla\n"
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
|
||||
listener(Logger::Level::Informational, "message");
|
||||
EXPECT_NE(buffer.str().find("INFO : message"), std::string::npos);
|
||||
std::cerr.rdbuf(old);
|
||||
std::cout.rdbuf(old);
|
||||
}
|
||||
|
||||
TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerUnknown)
|
||||
@ -198,15 +197,74 @@ TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerUnknown)
|
||||
EnvironmentLogLevelListener::SetInitialized(false);
|
||||
SetLogLevel("verbose");
|
||||
|
||||
// Error logging goes to cerr, all other logging goes to cout.
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cerr.rdbuf(buffer.rdbuf());
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
|
||||
std::string text = buffer.str(); // text will now contain "Bla\n"
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
|
||||
listener(static_cast<Logger::Level>(42), "message");
|
||||
EXPECT_NE(buffer.str().find("????? : message"), std::string::npos);
|
||||
std::cerr.rdbuf(old);
|
||||
std::cout.rdbuf(old);
|
||||
}
|
||||
|
||||
// Verify that the log listener inserts a crlf at the end of the message if none is provided.
|
||||
TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerWithNoCrlf)
|
||||
{
|
||||
EnvironmentLogLevelListener::SetInitialized(false);
|
||||
SetLogLevel("verbose");
|
||||
|
||||
// Error logging goes to cerr, all other logging goes to cout.
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
|
||||
listener(Logger::Level::Informational, "message");
|
||||
EXPECT_NE(buffer.str().find("INFO : message\n"), std::string::npos);
|
||||
std::cout.rdbuf(old);
|
||||
}
|
||||
|
||||
// Verify that the log listener does not insert a crlf at the end of the message if one is provided.
|
||||
TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerWithCrlf)
|
||||
{
|
||||
EnvironmentLogLevelListener::SetInitialized(false);
|
||||
SetLogLevel("verbose");
|
||||
|
||||
// Error logging goes to cerr, all other logging goes to cout.
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
|
||||
listener(Logger::Level::Informational, "message\n");
|
||||
EXPECT_NE(buffer.str().find("INFO : message\n"), std::string::npos);
|
||||
EXPECT_EQ(buffer.str().find("INFO : message\n\n"), std::string::npos);
|
||||
std::cout.rdbuf(old);
|
||||
}
|
||||
|
||||
// Verify that the log listener handles empty strings correctly.
|
||||
TEST_F(EnvironmentLogLevelListenerTest, GetLogListenerWithEmptyString)
|
||||
{
|
||||
EnvironmentLogLevelListener::SetInitialized(false);
|
||||
SetLogLevel("verbose");
|
||||
|
||||
std::stringstream buffer;
|
||||
// Error logging goes to cerr, all other logging goes to cout.
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
|
||||
auto listener = EnvironmentLogLevelListener::GetLogListener();
|
||||
|
||||
EXPECT_NE(listener, nullptr);
|
||||
|
||||
listener(Logger::Level::Informational, "");
|
||||
EXPECT_NE(buffer.str().find("INFO : \n"), std::string::npos);
|
||||
EXPECT_EQ(buffer.str().find("INFO : \n\n"), std::string::npos);
|
||||
std::cout.rdbuf(old);
|
||||
}
|
||||
|
||||
@ -202,3 +202,94 @@ TEST(Logger, Message)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Logger, LoggerStream)
|
||||
{
|
||||
Logger::Level level = Logger::Level::Error;
|
||||
std::string message;
|
||||
|
||||
Logger::SetListener([&](auto lvl, auto msg) {
|
||||
level = lvl;
|
||||
message = msg;
|
||||
});
|
||||
|
||||
Logger::SetLevel(Logger::Level::Verbose);
|
||||
{
|
||||
Log::Stream(Logger::Level::Verbose) << "Verbose" << std::endl;
|
||||
EXPECT_EQ(message, "Verbose\n");
|
||||
Log::Stream(Logger::Level::Informational) << "Informational" << std::endl;
|
||||
EXPECT_EQ(message, "Informational\n");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Warning) << "Warning" << std::endl;
|
||||
EXPECT_EQ(message, "Warning\n");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Error) << "Error" << std::endl;
|
||||
EXPECT_EQ(message, "Error\n");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Verbose) << "Test";
|
||||
// std::endl flushes the stream, so we need to manually flush the ostream.
|
||||
Log::Stream(Logger::Level::Verbose).flush();
|
||||
EXPECT_EQ(message, "Test");
|
||||
message.clear();
|
||||
}
|
||||
|
||||
Logger::SetLevel(Logger::Level::Informational);
|
||||
{
|
||||
Log::Stream(Logger::Level::Verbose) << "Verbose" << std::endl;
|
||||
EXPECT_EQ(message, "");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Informational) << "Informational" << std::endl;
|
||||
EXPECT_EQ(message, "Informational\n");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Warning) << "Warning" << std::endl;
|
||||
EXPECT_EQ(message, "Warning\n");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Error) << "Error" << std::endl;
|
||||
EXPECT_EQ(message, "Error\n");
|
||||
message.clear();
|
||||
}
|
||||
|
||||
Logger::SetLevel(Logger::Level::Warning);
|
||||
{
|
||||
Log::Stream(Logger::Level::Verbose) << "Verbose" << std::endl;
|
||||
EXPECT_EQ(message, "");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Informational) << "Informational" << std::endl;
|
||||
EXPECT_EQ(message, "");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Warning) << "Warning" << std::endl;
|
||||
EXPECT_EQ(message, "Warning\n");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Error) << "Error" << std::endl;
|
||||
EXPECT_EQ(message, "Error\n");
|
||||
message.clear();
|
||||
}
|
||||
|
||||
Logger::SetLevel(Logger::Level::Error);
|
||||
{
|
||||
Log::Stream(Logger::Level::Verbose) << "Verbose" << std::endl;
|
||||
EXPECT_EQ(message, "");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Informational) << "Informational" << std::endl;
|
||||
EXPECT_EQ(message, "");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Warning) << "Warning" << std::endl;
|
||||
EXPECT_EQ(message, "");
|
||||
message.clear();
|
||||
|
||||
Log::Stream(Logger::Level::Error) << "Error" << std::endl;
|
||||
EXPECT_EQ(message, "Error\n");
|
||||
message.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user