#include "logger.h"
#include <library/cpp/yt/assert/assert.h>
#include <library/cpp/yt/cpu_clock/clock.h>
#include <library/cpp/yt/misc/thread_name.h>
#include <util/system/compiler.h>
#include <util/system/thread.h>
namespace NYT::NLogging {
////////////////////////////////////////////////////////////////////////////////
namespace NDetail {
void OnCriticalLogEvent(
const TLogger& logger,
const TLogEvent& event)
{
if (event.Level == ELogLevel::Fatal ||
event.Level == ELogLevel::Alert && logger.GetAbortOnAlert())
{
fprintf(stderr, "*** Aborting on critical log event\n");
fwrite(event.MessageRef.begin(), 1, event.MessageRef.size(), stderr);
fprintf(stderr, "\n");
YT_ABORT();
}
}
TSharedRef TMessageStringBuilder::Flush()
{
return Buffer_.Slice(0, GetLength());
}
void TMessageStringBuilder::DisablePerThreadCache()
{
Cache_ = nullptr;
CacheDestroyed_ = true;
}
void TMessageStringBuilder::DoReset()
{
Buffer_.Reset();
}
void TMessageStringBuilder::DoReserve(size_t newCapacity)
{
auto oldLength = GetLength();
newCapacity = FastClp2(newCapacity);
auto newChunkSize = std::max(ChunkSize, newCapacity);
// Hold the old buffer until the data is copied.
auto oldBuffer = std::move(Buffer_);
auto* cache = GetCache();
if (Y_LIKELY(cache)) {
auto oldCapacity = End_ - Begin_;
auto deltaCapacity = newCapacity - oldCapacity;
if (End_ == cache->Chunk.Begin() + cache->ChunkOffset &&
cache->ChunkOffset + deltaCapacity <= cache->Chunk.Size())
{
// Resize inplace.
Buffer_ = cache->Chunk.Slice(cache->ChunkOffset - oldCapacity, cache->ChunkOffset + deltaCapacity);
cache->ChunkOffset += deltaCapacity;
End_ = Begin_ + newCapacity;
return;
}
if (Y_UNLIKELY(cache->ChunkOffset + newCapacity > cache->Chunk.Size())) {
cache->Chunk = TSharedMutableRef::Allocate<TMessageBufferTag>(newChunkSize, {.InitializeStorage = false});
cache->ChunkOffset = 0;
}
Buffer_ = cache->Chunk.Slice(cache->ChunkOffset, cache->ChunkOffset + newCapacity);
cache->ChunkOffset += newCapacity;
} else {
Buffer_ = TSharedMutableRef::Allocate<TMessageBufferTag>(newChunkSize, {.InitializeStorage = false});
newCapacity = newChunkSize;
}
if (oldLength > 0) {
::memcpy(Buffer_.Begin(), Begin_, oldLength);
}
Begin_ = Buffer_.Begin();
End_ = Begin_ + newCapacity;
}
TMessageStringBuilder::TPerThreadCache* TMessageStringBuilder::GetCache()
{
if (Y_LIKELY(Cache_)) {
return Cache_;
}
if (CacheDestroyed_) {
return nullptr;
}
static thread_local TPerThreadCache Cache;
Cache_ = &Cache;
return Cache_;
}
TMessageStringBuilder::TPerThreadCache::~TPerThreadCache()
{
TMessageStringBuilder::DisablePerThreadCache();
}
thread_local TMessageStringBuilder::TPerThreadCache* TMessageStringBuilder::Cache_;
thread_local bool TMessageStringBuilder::CacheDestroyed_;
} // namespace NDetail
////////////////////////////////////////////////////////////////////////////////
Y_WEAK TLoggingContext GetLoggingContext()
{
return {
.Instant = GetCpuInstant(),
.ThreadId = TThread::CurrentThreadId(),
.ThreadName = GetCurrentThreadName(),
};
}
Y_WEAK ILogManager* GetDefaultLogManager()
{
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
thread_local ELogLevel ThreadMinLogLevel = ELogLevel::Minimum;
void SetThreadMinLogLevel(ELogLevel minLogLevel)
{
ThreadMinLogLevel = minLogLevel;
}
ELogLevel GetThreadMinLogLevel()
{
return ThreadMinLogLevel;
}
////////////////////////////////////////////////////////////////////////////////
TLogger::TLogger(ILogManager* logManager, TStringBuf categoryName)
: LogManager_(logManager)
, Category_(LogManager_ ? LogManager_->GetCategory(categoryName) : nullptr)
, MinLevel_(LogManager_ ? LoggerDefaultMinLevel : NullLoggerMinLevel)
{ }
TLogger::TLogger(TStringBuf categoryName)
: TLogger(GetDefaultLogManager(), categoryName)
{ }
TLogger::operator bool() const
{
return LogManager_;
}
const TLoggingCategory* TLogger::GetCategory() const
{
return Category_;
}
bool TLogger::IsLevelEnabledHeavy(ELogLevel level) const
{
// Note that we managed to reach this point, i.e. level >= MinLevel_,
// which implies that MinLevel_ != ELogLevel::Maximum, so this logger was not
// default constructed, thus it has non-trivial category.
YT_ASSERT(Category_);
if (Category_->CurrentVersion != Category_->ActualVersion->load(std::memory_order::relaxed)) {
LogManager_->UpdateCategory(const_cast<TLoggingCategory*>(Category_));
}
return
level >= Category_->MinPlainTextLevel &&
level >= ThreadMinLogLevel;
}
bool TLogger::GetAbortOnAlert() const
{
return LogManager_->GetAbortOnAlert();
}
bool TLogger::IsEssential() const
{
return Essential_;
}
void TLogger::UpdateAnchor(TLoggingAnchor* anchor) const
{
LogManager_->UpdateAnchor(anchor);
}
void TLogger::RegisterStaticAnchor(TLoggingAnchor* anchor, ::TSourceLocation sourceLocation, TStringBuf message) const
{
LogManager_->RegisterStaticAnchor(anchor, sourceLocation, message);
}
void TLogger::Write(TLogEvent&& event) const
{
LogManager_->Enqueue(std::move(event));
}
void TLogger::AddRawTag(const TString& tag)
{
if (!Tag_.empty()) {
Tag_ += ", ";
}
Tag_ += tag;
}
TLogger TLogger::WithRawTag(const TString& tag) const
{
auto result = *this;
result.AddRawTag(tag);
return result;
}
TLogger TLogger::WithEssential(bool essential) const
{
auto result = *this;
result.Essential_ = essential;
return result;
}
TLogger TLogger::WithStructuredValidator(TStructuredValidator validator) const
{
auto result = *this;
result.StructuredValidators_.push_back(std::move(validator));
return result;
}
TLogger TLogger::WithMinLevel(ELogLevel minLevel) const
{
auto result = *this;
if (result) {
result.MinLevel_ = minLevel;
}
return result;
}
const TString& TLogger::GetTag() const
{
return Tag_;
}
const TLogger::TStructuredTags& TLogger::GetStructuredTags() const
{
return StructuredTags_;
}
const TLogger::TStructuredValidators& TLogger::GetStructuredValidators() const
{
return StructuredValidators_;
}
////////////////////////////////////////////////////////////////////////////////
void LogStructuredEvent(
const TLogger& logger,
NYson::TYsonString message,
ELogLevel level)
{
YT_VERIFY(message.GetType() == NYson::EYsonType::MapFragment);
if (!logger.GetStructuredValidators().empty()) {
auto samplingRate = logger.GetCategory()->StructuredValidationSamplingRate.load();
auto p = RandomNumber<double>();
if (p < samplingRate) {
for (const auto& validator : logger.GetStructuredValidators()) {
validator(message);
}
}
}
auto loggingContext = GetLoggingContext();
auto event = NDetail::CreateLogEvent(
loggingContext,
logger,
level);
event.MessageKind = ELogMessageKind::Structured;
event.MessageRef = message.ToSharedRef();
event.Family = ELogFamily::Structured;
logger.Write(std::move(event));
}
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NLogging