#include "formatter.h"
#include <library/cpp/yt/cpu_clock/clock.h>
#include <library/cpp/yt/misc/port.h>
#ifdef YT_USE_SSE42
#include <emmintrin.h>
#include <pmmintrin.h>
#endif
namespace NYT::NLogging {
constexpr int MessageBufferWatermarkSize = 256;
////////////////////////////////////////////////////////////////////////////////
namespace {
// Ultra-fast specialized versions of AppendNumber.
void AppendDigit(TBaseFormatter* out, ui32 value)
{
out->AppendChar('0' + value);
}
void AppendNumber2(TBaseFormatter* out, ui32 value)
{
AppendDigit(out, value / 10);
AppendDigit(out, value % 10);
}
void AppendNumber3(TBaseFormatter* out, ui32 value)
{
AppendDigit(out, value / 100);
AppendDigit(out, (value / 10) % 10);
AppendDigit(out, value % 10);
}
void AppendNumber4(TBaseFormatter* out, ui32 value)
{
AppendDigit(out, value / 1000);
AppendDigit(out, (value / 100) % 10);
AppendDigit(out, (value / 10) % 10);
AppendDigit(out, value % 10);
}
void AppendNumber6(TBaseFormatter* out, ui32 value)
{
AppendDigit(out, value / 100000);
AppendDigit(out, (value / 10000) % 10);
AppendDigit(out, (value / 1000) % 10);
AppendDigit(out, (value / 100) % 10);
AppendDigit(out, (value / 10) % 10);
AppendDigit(out, value % 10);
}
} // namespace
void FormatDateTime(TBaseFormatter* out, TInstant dateTime)
{
tm localTime;
dateTime.LocalTime(&localTime);
AppendNumber4(out, localTime.tm_year + 1900);
out->AppendChar('-');
AppendNumber2(out, localTime.tm_mon + 1);
out->AppendChar('-');
AppendNumber2(out, localTime.tm_mday);
out->AppendChar(' ');
AppendNumber2(out, localTime.tm_hour);
out->AppendChar(':');
AppendNumber2(out, localTime.tm_min);
out->AppendChar(':');
AppendNumber2(out, localTime.tm_sec);
}
void FormatMilliseconds(TBaseFormatter* out, TInstant dateTime)
{
AppendNumber3(out, dateTime.MilliSecondsOfSecond());
}
void FormatMicroseconds(TBaseFormatter* out, TInstant dateTime)
{
AppendNumber6(out, dateTime.MicroSecondsOfSecond());
}
void FormatLevel(TBaseFormatter* out, ELogLevel level)
{
static char chars[] = "?TDIWEAF?";
out->AppendChar(chars[static_cast<int>(level)]);
}
void FormatMessage(TBaseFormatter* out, TStringBuf message)
{
auto current = message.begin();
#ifdef YT_USE_SSE42
auto vectorLow = _mm_set1_epi8(PrintableASCIILow);
auto vectorHigh = _mm_set1_epi8(PrintableASCIIHigh);
#endif
auto appendChar = [&] {
char ch = *current;
if (ch == '\n') {
out->AppendString("\\n");
} else if (ch == '\t') {
out->AppendString("\\t");
} else if (ch < PrintableASCIILow || ch > PrintableASCIIHigh) {
unsigned char unsignedCh = ch;
out->AppendString("\\x");
out->AppendChar(IntToHexLowercase[unsignedCh >> 4]);
out->AppendChar(IntToHexLowercase[unsignedCh & 15]);
} else {
out->AppendChar(ch);
}
++current;
};
while (current < message.end()) {
if (out->GetBytesRemaining() < MessageBufferWatermarkSize) {
out->AppendString(TStringBuf("...<message truncated>"));
break;
}
#ifdef YT_USE_SSE42
// Use SSE for optimization.
if (current + 16 > message.end()) {
appendChar();
} else {
const void* inPtr = &(*current);
void* outPtr = out->GetCursor();
auto value = _mm_lddqu_si128(static_cast<const __m128i*>(inPtr));
if (_mm_movemask_epi8(_mm_cmplt_epi8(value, vectorLow)) ||
_mm_movemask_epi8(_mm_cmpgt_epi8(value, vectorHigh))) {
for (int index = 0; index < 16; ++index) {
appendChar();
}
} else {
_mm_storeu_si128(static_cast<__m128i*>(outPtr), value);
out->Advance(16);
current += 16;
}
}
#else
// Unoptimized version.
appendChar();
#endif
}
}
////////////////////////////////////////////////////////////////////////////////
void TCachingDateFormatter::Format(TBaseFormatter* buffer, TInstant dateTime, bool printMicroseconds)
{
auto currentSecond = dateTime.Seconds();
if (CachedSecond_ != currentSecond) {
Cached_.Reset();
FormatDateTime(&Cached_, dateTime);
CachedSecond_ = currentSecond;
}
buffer->AppendString(Cached_.GetBuffer());
buffer->AppendChar(',');
if (printMicroseconds) {
FormatMicroseconds(buffer, dateTime);
} else {
FormatMilliseconds(buffer, dateTime);
}
}
////////////////////////////////////////////////////////////////////////////////
TPlainTextEventFormatter::TPlainTextEventFormatter(bool enableSourceLocation)
: EnableSourceLocation_(enableSourceLocation)
{ }
void TPlainTextEventFormatter::Format(TBaseFormatter* buffer, const TLogEvent& event)
{
CachingDateFormatter_.Format(buffer, CpuInstantToInstant(event.Instant), true);
buffer->AppendChar('\t');
FormatLevel(buffer, event.Level);
buffer->AppendChar('\t');
buffer->AppendString(event.Category->Name);
buffer->AppendChar('\t');
FormatMessage(buffer, event.MessageRef.ToStringBuf());
buffer->AppendChar('\t');
if (event.ThreadName.Length > 0) {
buffer->AppendString(TStringBuf(event.ThreadName.Buffer.data(), event.ThreadName.Length));
} else if (event.ThreadId != TThreadId()) {
buffer->AppendNumber(event.ThreadId, 16);
}
buffer->AppendChar('\t');
if (event.FiberId != TFiberId()) {
buffer->AppendNumber(event.FiberId, 16);
}
buffer->AppendChar('\t');
if (event.TraceId != TTraceId()) {
buffer->AppendGuid(event.TraceId);
}
if (EnableSourceLocation_) {
buffer->AppendChar('\t');
if (event.SourceFile) {
auto sourceFile = event.SourceFile;
buffer->AppendString(sourceFile.RNextTok(LOCSLASH_C));
buffer->AppendChar(':');
buffer->AppendNumber(event.SourceLine);
}
}
buffer->AppendChar('\n');
}
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NLogging