blob: 672e423617625dce6ad13457f7a7c3b21f0a50c8 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <fcntl.h>
#include <lib/stdcompat/variant.h>
#include <lib/syslog/structured_backend/cpp/fuchsia_syslog.h>
#include <lib/syslog/structured_backend/fuchsia_syslog.h>
#include <lib/zx/channel.h>
#include <lib/zx/clock.h>
#include <lib/zx/process.h>
#include <lib/zx/socket.h>
#include <lib/zx/time.h>
#include <fstream>
#include <iostream>
#include <sstream>
namespace {
// Represents a byte offset that has no alignment guarantees.
class ByteOffset final {
public:
ByteOffset(const ByteOffset& other) = default;
ByteOffset(ByteOffset&& other) = default;
static ByteOffset FromBuffer(size_t offset, size_t capacity) {
return ByteOffset(offset, capacity).AssertValid();
}
static ByteOffset Unbounded(size_t offset) { return ByteOffset(offset, -1); }
size_t unsafe_get() const { return value_; }
size_t capacity() const { return capacity_; }
ByteOffset AssertAlignedTo(size_t size) const {
assert((value_ % size) == 0);
if (!((value_ % size) == 0)) {
abort();
}
return *this;
}
ByteOffset& operator=(ByteOffset&& other) = default;
ByteOffset& operator=(const ByteOffset& other) = default;
ByteOffset operator+(size_t offset) const {
return ByteOffset(value_ + offset, capacity_).AssertValid();
}
private:
ByteOffset(size_t value, size_t capacity) : value_(value), capacity_(capacity) {}
ByteOffset& AssertValid() {
if (!((capacity_ == 0) && (value_ == 0))) {
assert(value_ < capacity_);
if (!(value_ < capacity_)) {
abort();
}
}
return *this;
}
size_t value_;
size_t capacity_;
};
} // namespace
namespace fuchsia_syslog {
// A word offset, which guarantees that the pointer is
// aligned to alignof(T). Operations are done with respect
// to words, not bytes.
template <typename T>
class WordOffset final {
public:
WordOffset() = delete;
WordOffset& operator=(const WordOffset& other) = default;
WordOffset(const WordOffset& other) {
capacity_ = other.capacity_;
value_ = other.value_;
}
WordOffset operator+(size_t offset) const {
return WordOffset(value_ + offset, capacity_).AssertValid();
}
WordOffset operator+(WordOffset offset) const {
return WordOffset(value_ + offset.value_, capacity_).AssertValid();
}
WordOffset operator++(int) const { return WordOffset(value_ + 1, capacity_).AssertValid(); }
WordOffset AddPadded(const ByteOffset& byte_offset) {
size_t needs_padding = (byte_offset.unsafe_get() % sizeof(T)) > 0;
// Multiply by needs_padding to set padding to 0 if no padding
// is necessary. This avoids unnecessary branching.
return WordOffset(value_ + (byte_offset.unsafe_get() / sizeof(T)) + needs_padding, capacity_);
}
WordOffset begin() { return WordOffset(0, capacity_); }
size_t capacity() { return capacity_; }
static WordOffset FromByteOffset(const ByteOffset& value) {
return WordOffset(value.AssertAlignedTo(sizeof(T)).unsafe_get() / sizeof(T),
value.capacity() / sizeof(T));
}
size_t unsafe_get() const { return value_; }
bool in_bounds(WordOffset<T> offset) { return !((offset.value_ + value_) >= capacity_); }
ByteOffset ToByteOffset() const {
return ByteOffset::FromBuffer(value_ * sizeof(T), capacity_ * sizeof(T));
}
void reset() { value_ = 0; }
static WordOffset invalid() { return WordOffset(0, 0); }
private:
WordOffset(size_t value, size_t capacity) : capacity_(capacity), value_(value) {}
WordOffset& AssertValid() {
if (!((capacity_ == 0) && (value_ == 0))) {
if (!(value_ < capacity_)) {
abort();
}
assert(value_ < capacity_);
}
return *this;
}
size_t capacity_;
size_t value_;
};
template <typename T>
WordOffset<T> WritePaddedInternal(T* buffer, const void* msg, const ByteOffset& length) {
if (length.unsafe_get() == 0) {
return WordOffset<T>::FromByteOffset(ByteOffset::Unbounded(0));
}
size_t needs_padding = (length.unsafe_get() % sizeof(T)) > 0;
size_t padding = sizeof(T) - (length.unsafe_get() % sizeof(T));
// Multiply by needs_padding to set padding to 0 if no padding
// is necessary. This avoids unnecessary branching.
padding *= needs_padding;
// If we added padding -- zero the padding bytes in a single write operation
size_t is_nonzero_length = length.unsafe_get() != 0;
size_t eof_in_bytes = length.unsafe_get() + padding;
size_t eof_in_words = eof_in_bytes / sizeof(T);
size_t last_word = eof_in_words - is_nonzero_length;
// Set the last word in the buffer to zero before writing
// the data to it if we added padding. If we didn't add padding,
// multiply by 1 which ends up writing back the current contents of that word
// resulting in a NOP.
buffer[last_word] *= !needs_padding;
memcpy(buffer, msg, length.unsafe_get());
return WordOffset<T>::FromByteOffset(ByteOffset::Unbounded(length.unsafe_get() + padding));
}
// Bitfield definitions copy-pasted from
// https://fuchsia.googlesource.com/fuchsia/+/c81451cd683e/sdk/lib/syslog/streams/cpp/fields.h.
template <size_t begin, size_t end>
struct Field final {
static_assert(begin < sizeof(uint64_t) * 8, "begin is out of bounds");
static_assert(end < sizeof(uint64_t) * 8, "end is out of bounds");
static_assert(begin <= end, "begin must not be larger than end");
static_assert(end - begin + 1 < 64, "must be a part of a word, not a whole word");
static constexpr uint64_t kMask = (uint64_t(1) << (end - begin + 1)) - 1;
template <typename T>
static constexpr uint64_t Make(T value) {
return static_cast<uint64_t>(value) << begin;
}
template <typename U>
static constexpr U Get(uint64_t word) {
return static_cast<U>((word >> (begin % 64)) & kMask);
}
static constexpr void Set(uint64_t* word, uint64_t value) {
*word = (*word & ~(kMask << begin)) | (value << begin);
}
};
// HeaderField structure for a Record
// see https://fuchsia.dev/fuchsia-src/reference/diagnostics/logs/encoding?hl=en#header
struct HeaderFields final {
using Type = Field<0, 3>;
using SizeWords = Field<4, 15>;
using Reserved = Field<16, 55>;
using Severity = Field<56, 63>;
};
// ArgumentField structure for an Argument
// see https://fuchsia.dev/fuchsia-src/reference/diagnostics/logs/encoding?hl=en#arguments
struct ArgumentFields {
using Type = Field<0, 3>;
using SizeWords = Field<4, 15>;
using NameRefVal = Field<16, 30>;
using NameRefMSB = Field<31, 31>;
};
struct BoolArgumentFields final : ArgumentFields {
using Value = Field<32, 32>;
};
struct StringArgumentFields final : ArgumentFields {
using ValueRef = Field<32, 47>;
};
struct ReservedFields final : ArgumentFields {
using Value = Field<32, 63>;
};
using log_word_t = uint64_t;
// Represents a slice of a buffer of type T.
template <typename T>
class DataSlice final {
public:
DataSlice(T* ptr, WordOffset<T> slice) : ptr_(ptr), slice_(slice) {}
T& operator[](WordOffset<T> offset) {
offset.AssertValid();
return ptr_[offset];
}
const T& operator[](WordOffset<T> offset) const { return ptr_[offset.unsafe_get()]; }
WordOffset<T> slice() { return slice_; }
T* data() { return ptr_; }
private:
T* ptr_;
WordOffset<T> slice_;
};
static DataSlice<const char> SliceFromString(const cpp17::string_view& string) {
return DataSlice<const char>(
string.data(), WordOffset<const char>::FromByteOffset(ByteOffset::Unbounded(string.size())));
}
template <typename T, size_t size>
static DataSlice<const T> SliceFromArray(const T (&array)[size]) {
return DataSlice<const T>(array, size);
}
template <size_t size>
static DataSlice<const char> SliceFromArray(const char (&array)[size]) {
return DataSlice<const char>(
array, WordOffset<const char>::FromByteOffset(ByteOffset::Unbounded(size - 1)));
}
struct RecordState final {
RecordState()
: arg_size(WordOffset<log_word_t>::FromByteOffset(
ByteOffset::FromBuffer(0, sizeof(fuchsia_syslog_log_buffer_t::data)))),
current_key_size(ByteOffset::FromBuffer(0, sizeof(fuchsia_syslog_log_buffer_t::data))),
cursor(WordOffset<log_word_t>::FromByteOffset(
ByteOffset::FromBuffer(0, sizeof(fuchsia_syslog_log_buffer_t::data)))) {}
// Header of the record itself
uint64_t* header;
// The corrected severity -- FIDL doesn't permit sending
// arbitrary values for enums, and the Rust side decodes this
// into a FIDL type, because of this we need to correct the severity
// to a valid FIDL severity and encode the raw severity separately
// so that Rust can decode it properly.
FuchsiaLogSeverity corrected_severity;
FuchsiaLogSeverity raw_severity;
// arg_size in words
WordOffset<log_word_t> arg_size;
zx::unowned_socket socket;
// key_size in bytes
ByteOffset current_key_size;
// Header of the current argument being encoded
uint64_t* current_header_position = 0;
uint32_t dropped_count = 0;
// Current position (in 64-bit words) into the buffer.
WordOffset<log_word_t> cursor;
// True if encoding was successful, false otherwise
bool encode_success = true;
// True if end was called
bool ended = false;
// Message string -- valid if severity is FATAL. For FATAL
// logs the caller is responsible for ensuring the string
// is valid for the duration of the call (which our macros
// will ensure for current users).
cpp17::optional<cpp17::string_view> msg_string;
static RecordState* CreatePtr(fuchsia_syslog_log_buffer_t* buffer) {
return reinterpret_cast<RecordState*>(&buffer->record_state);
}
};
static_assert(sizeof(RecordState) <= sizeof(fuchsia_syslog_log_buffer_t::record_state),
"Expected sizeof(RecordState) <= sizeof(LogBuffer::record_state)");
static_assert(std::alignment_of<RecordState>() == sizeof(uint64_t),
"Expected std::alignment_of<RecordState>() == sizeof(uint64_t)");
// Used for accessing external data buffers provided by clients.
// Used by the Encoder to do in-place encoding of data
class ExternalDataBuffer final {
public:
explicit ExternalDataBuffer(fuchsia_syslog_log_buffer_t* buffer)
: buffer_(&buffer->data[0]), cursor_(RecordState::CreatePtr(buffer)->cursor) {}
ExternalDataBuffer(log_word_t* data, size_t length, WordOffset<log_word_t>& cursor)
: buffer_(data), cursor_(cursor) {}
__WARN_UNUSED_RESULT bool Write(const log_word_t* data, WordOffset<log_word_t> length) {
if (!cursor_.in_bounds(length)) {
// TODO (https://fxbug.dev/80984): Add test for this.
return false;
}
for (size_t i = 0; i < length.unsafe_get(); i++) {
buffer_[(cursor_ + i).unsafe_get()] = data[i];
}
cursor_ = cursor_ + length;
return true;
}
__WARN_UNUSED_RESULT bool WritePadded(const void* msg, const ByteOffset& byte_count,
WordOffset<log_word_t>* written) {
assert(written != nullptr);
WordOffset<log_word_t> word_count = cursor_.begin().AddPadded(byte_count);
if (!cursor_.in_bounds(word_count)) {
// TODO (https://fxbug.dev/80984): Add test for this.
return false;
}
auto retval = WritePaddedInternal(buffer_ + cursor_.unsafe_get(), msg, byte_count);
cursor_ = cursor_ + retval;
*written = retval;
return true;
}
template <typename T>
__WARN_UNUSED_RESULT bool Write(const T& data) {
static_assert(sizeof(T) >= sizeof(log_word_t), "Expected sizeof(T) >= sizeof(log_word_t)");
static_assert(alignof(T) >= sizeof(log_word_t), "Expected alignof(T) >= sizeof(log_word_t)");
return Write(reinterpret_cast<const log_word_t*>(&data),
WordOffset<log_word_t>::FromByteOffset(
ByteOffset::Unbounded((sizeof(T) / sizeof(log_word_t)) * sizeof(log_word_t))));
}
uint64_t* data() { return buffer_ + cursor_.unsafe_get(); }
DataSlice<log_word_t> GetSlice() { return DataSlice<log_word_t>(buffer_, cursor_); }
private:
// Start of buffer
log_word_t* buffer_ = nullptr;
// Current location in buffer (in words)
WordOffset<log_word_t>& cursor_;
};
// Encoder for structured logs
template <typename T>
class Encoder final {
public:
explicit Encoder(T& buffer) { buffer_ = &buffer; }
// Begins the log record.
void Begin(RecordState& state, zx::time timestamp, FuchsiaLogSeverity severity) {
state.raw_severity = severity;
state.header = buffer_->data();
log_word_t empty_header = 0;
state.encode_success &= buffer_->Write(empty_header);
state.encode_success &= buffer_->Write(timestamp.get());
}
// Flushes a previous argument after it has been fully encoded.
void FlushPreviousArgument(RecordState& state) { state.arg_size.reset(); }
// Appends the key portion of an argument to the encode buffer.
void AppendArgumentKey(RecordState& state, DataSlice<const char> key) {
FlushPreviousArgument(state);
auto header_position = buffer_->data();
log_word_t empty_header = 0;
state.encode_success &= buffer_->Write(empty_header);
WordOffset<log_word_t> s_size =
WordOffset<log_word_t>::FromByteOffset(ByteOffset::Unbounded(0));
state.encode_success &= buffer_->WritePadded(key.data(), key.slice().ToByteOffset(), &s_size);
state.arg_size = s_size + 1; // offset by 1 for the header
state.current_key_size = key.slice().ToByteOffset();
state.current_header_position = header_position;
}
// Generates an argument header
uint64_t ComputeArgHeader(RecordState& state, int type) {
return ArgumentFields::Type::Make(type) |
ArgumentFields::SizeWords::Make(state.arg_size.unsafe_get()) |
ArgumentFields::NameRefVal::Make(state.current_key_size.unsafe_get()) |
ArgumentFields::NameRefMSB::Make(state.current_key_size.unsafe_get() > 0 ? 1 : 0) |
ReservedFields::Value::Make(0);
;
}
// Append a value to the current argument
void AppendArgumentValue(RecordState& state, int64_t value) {
// int64
int type = 3;
state.encode_success &= buffer_->Write(value);
state.arg_size++;
*state.current_header_position = ComputeArgHeader(state, type);
}
// Append a value to the current argument
void AppendArgumentValue(RecordState& state, uint64_t value) {
// uint64
int type = 4;
state.encode_success &= buffer_->Write(value);
state.arg_size = state.arg_size++;
*state.current_header_position = ComputeArgHeader(state, type);
}
// Append a value to the current argument
void AppendArgumentValue(RecordState& state, double value) {
// double
int type = 5;
state.encode_success &= buffer_->Write(value);
state.arg_size = state.arg_size++;
*state.current_header_position = ComputeArgHeader(state, type);
}
// Append a value to the current argument
void AppendArgumentValue(RecordState& state, DataSlice<const char> string) {
// string
int type = 6;
WordOffset<log_word_t> written =
WordOffset<log_word_t>::FromByteOffset(ByteOffset::Unbounded(0));
state.encode_success &=
buffer_->WritePadded(string.data(), string.slice().ToByteOffset(), &written);
state.arg_size = state.arg_size + written;
uint64_t value_ref =
string.slice().unsafe_get() > 0 ? (1 << 15) | string.slice().unsafe_get() : 0;
*state.current_header_position =
ComputeArgHeader(state, type) | StringArgumentFields::ValueRef::Make(value_ref);
}
// Append a value to the current argument
void AppendArgumentValue(RecordState& state, bool value) {
// bool
int type = 9;
*state.current_header_position = ComputeArgHeader(state, type) |
BoolArgumentFields::Value::Make(static_cast<uint64_t>(value));
}
// Append a value to the current argument
void End(RecordState& state) {
// See src/lib/diagnostics/stream/rust/src/lib.rs
constexpr auto kTracingFormatLogRecordType = 9;
FlushPreviousArgument(state);
uint64_t header =
HeaderFields::Type::Make(kTracingFormatLogRecordType) |
HeaderFields::SizeWords::Make(static_cast<size_t>(buffer_->data() - state.header)) |
HeaderFields::Reserved::Make(0) | HeaderFields::Severity::Make(state.raw_severity);
*state.header = header;
}
private:
T* buffer_;
};
// Compiler thinks this is unused even though WriteLogToSocket uses it.
__UNUSED const char kMessageFieldName[] = "message";
const char kPrintfFieldName[] = "printf";
const char kVerbosityFieldName[] = "verbosity";
const char kPidFieldName[] = "pid";
const char kTidFieldName[] = "tid";
const char kDroppedLogsFieldName[] = "dropped_logs";
const char kFileFieldName[] = "file";
const char kLineFieldName[] = "line";
void BeginRecordInternal(fuchsia_syslog_log_buffer_t* buffer, FuchsiaLogSeverity severity,
cpp17::optional<cpp17::string_view> file_name, unsigned int line,
cpp17::optional<cpp17::string_view> msg,
cpp17::optional<cpp17::string_view> condition, bool is_printf,
zx::unowned_socket socket, uint32_t dropped_count, zx_koid_t pid,
zx_koid_t tid) {
cpp17::optional<int8_t> raw_severity;
// Validate that the severity matches the FIDL definition in
// sdk/fidl/fuchsia.diagnostics/severity.fidl.
if ((severity % 0x10) || (severity > 0x60) || (severity < 0x10)) {
raw_severity = severity;
severity = FUCHSIA_LOG_DEBUG;
}
// Initialize the encoder targeting the passed buffer, and begin the record.
auto time = zx::clock::get_monotonic();
auto* state = RecordState::CreatePtr(buffer);
RecordState& record = *state;
// Invoke the constructor of RecordState to construct a valid RecordState
// inside the LogBuffer.
new (state) RecordState;
state->socket = std::move(socket);
if (severity == FUCHSIA_LOG_FATAL) {
state->msg_string = msg;
}
state->corrected_severity = severity;
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
encoder.Begin(*state, time, severity);
if (is_printf) {
encoder.AppendArgumentKey(record, SliceFromArray(kPrintfFieldName));
encoder.AppendArgumentValue(record, static_cast<uint64_t>(0));
}
// Initialize common PID/TID fields
encoder.AppendArgumentKey(record, SliceFromArray(kPidFieldName));
encoder.AppendArgumentValue(record, static_cast<uint64_t>(pid));
encoder.AppendArgumentKey(record, SliceFromArray(kTidFieldName));
encoder.AppendArgumentValue(record, static_cast<uint64_t>(tid));
record.dropped_count = dropped_count;
// Initialize optional fields if set.
if (raw_severity) {
encoder.AppendArgumentKey(record, SliceFromString(kVerbosityFieldName));
encoder.AppendArgumentValue(record, static_cast<int64_t>(raw_severity.value()));
}
if (dropped_count) {
encoder.AppendArgumentKey(record, SliceFromString(kDroppedLogsFieldName));
encoder.AppendArgumentValue(record, static_cast<uint64_t>(dropped_count));
}
if (msg) {
encoder.AppendArgumentKey(record, SliceFromString(kMessageFieldName));
encoder.AppendArgumentValue(record, SliceFromString(*msg));
}
if (file_name) {
encoder.AppendArgumentKey(record, SliceFromString(kFileFieldName));
encoder.AppendArgumentValue(record, SliceFromString(*file_name));
}
encoder.AppendArgumentKey(record, SliceFromString(kLineFieldName));
encoder.AppendArgumentValue(record, static_cast<uint64_t>(line));
}
void BeginRecord(fuchsia_syslog_log_buffer_t* buffer, FuchsiaLogSeverity severity,
cpp17::optional<cpp17::string_view> file_name, unsigned int line,
cpp17::optional<cpp17::string_view> message,
cpp17::optional<cpp17::string_view> condition, bool is_printf,
zx::unowned_socket socket, uint32_t dropped_count, zx_koid_t pid, zx_koid_t tid) {
BeginRecordInternal(buffer, severity, file_name, line, message, condition, is_printf,
std::move(socket), dropped_count, pid, tid);
}
void WriteKeyValue(fuchsia_syslog_log_buffer_t* buffer, cpp17::string_view key,
cpp17::string_view value) {
auto* state = RecordState::CreatePtr(buffer);
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
encoder.AppendArgumentKey(
*state, DataSlice<const char>(key.data(), WordOffset<const char>::FromByteOffset(
ByteOffset::Unbounded(key.size()))));
encoder.AppendArgumentValue(
*state, DataSlice<const char>(value.data(), WordOffset<const char>::FromByteOffset(
ByteOffset::Unbounded(value.size()))));
}
void WriteKeyValue(fuchsia_syslog_log_buffer_t* buffer, cpp17::string_view key, int64_t value) {
auto* state = RecordState::CreatePtr(buffer);
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
encoder.AppendArgumentKey(
*state, DataSlice<const char>(key.data(), WordOffset<const char>::FromByteOffset(
ByteOffset::Unbounded(key.size()))));
encoder.AppendArgumentValue(*state, value);
}
void WriteKeyValue(fuchsia_syslog_log_buffer_t* buffer, cpp17::string_view key, uint64_t value) {
auto* state = RecordState::CreatePtr(buffer);
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
encoder.AppendArgumentKey(
*state, DataSlice<const char>(key.data(), WordOffset<const char>::FromByteOffset(
ByteOffset::Unbounded(key.size()))));
encoder.AppendArgumentValue(*state, value);
}
void WriteKeyValue(fuchsia_syslog_log_buffer_t* buffer, cpp17::string_view key, double value) {
auto* state = RecordState::CreatePtr(buffer);
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
encoder.AppendArgumentKey(
*state, DataSlice<const char>(key.data(), WordOffset<const char>::FromByteOffset(
ByteOffset::Unbounded(key.size()))));
encoder.AppendArgumentValue(*state, value);
}
template <typename V, typename = std::enable_if_t<std::is_same<V, bool>{}>>
void WriteKeyValue(fuchsia_syslog_log_buffer_t* buffer, cpp17::string_view key, V value) {
auto* state = RecordState::CreatePtr(buffer);
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
encoder.AppendArgumentKey(
*state, DataSlice<const char>(key.data(), WordOffset<const char>::FromByteOffset(
ByteOffset::Unbounded(key.size()))));
encoder.AppendArgumentValue(*state, value);
}
void EndRecord(fuchsia_syslog_log_buffer_t* buffer) {
auto* state = RecordState::CreatePtr(buffer);
if (state->ended) {
return;
}
state->ended = true;
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
encoder.End(*state);
}
bool FlushRecord(fuchsia_syslog_log_buffer_t* buffer) {
auto* state = RecordState::CreatePtr(buffer);
if (!state->encode_success) {
return false;
}
ExternalDataBuffer external_buffer(buffer);
Encoder<ExternalDataBuffer> encoder(external_buffer);
auto slice = external_buffer.GetSlice();
auto status =
state->socket->write(0, slice.data(), slice.slice().ToByteOffset().unsafe_get(), nullptr);
return status != ZX_ERR_BAD_STATE && status != ZX_ERR_PEER_CLOSED;
}
} // namespace fuchsia_syslog
static cpp17::optional<cpp17::string_view> CStringToStringView(const char* str, size_t len) {
if (!str) {
return cpp17::nullopt;
}
return cpp17::string_view(str, len);
}
void syslog_begin_record_transitional(fuchsia_syslog_log_buffer_t* buffer,
FuchsiaLogSeverity severity, const char* file_name,
size_t file_name_length, unsigned int line,
const char* message, size_t message_length, bool is_printf,
zx_handle_t socket, uint32_t dropped_count, zx_koid_t pid,
zx_koid_t tid) {
fuchsia_syslog::BeginRecord(buffer, severity, CStringToStringView(file_name, file_name_length),
line, CStringToStringView(message, message_length), cpp17::nullopt,
is_printf, zx::unowned_socket(socket), dropped_count, pid, tid);
}
__BEGIN_CDECLS
void syslog_begin_record(fuchsia_syslog_log_buffer_t* buffer, FuchsiaLogSeverity severity,
const char* file_name, size_t file_name_length, unsigned int line,
const char* message, size_t message_length, const char* condition,
size_t condition_length, bool is_printf, zx_handle_t socket,
uint32_t dropped_count, zx_koid_t pid, zx_koid_t tid) {
fuchsia_syslog::BeginRecord(buffer, severity, CStringToStringView(file_name, file_name_length),
line, CStringToStringView(message, message_length),
CStringToStringView(condition, condition_length), is_printf,
zx::unowned_socket(socket), dropped_count, pid, tid);
}
// Writes a key/value pair to the buffer.
void syslog_write_key_value_string(fuchsia_syslog_log_buffer_t* buffer, const char* key,
size_t key_length, const char* value, size_t value_length) {
fuchsia_syslog::WriteKeyValue(buffer, CStringToStringView(key, key_length).value(),
CStringToStringView(value, value_length).value());
}
// Writes a key/value pair to the buffer.
void syslog_write_key_value_int64(fuchsia_syslog_log_buffer_t* buffer, const char* key,
size_t key_length, int64_t value) {
fuchsia_syslog::WriteKeyValue(buffer, CStringToStringView(key, key_length).value(), value);
}
// Writes a key/value pair to the buffer.
void syslog_write_key_value_uint64(fuchsia_syslog_log_buffer_t* buffer, const char* key,
size_t key_length, uint64_t value) {
fuchsia_syslog::WriteKeyValue(buffer, CStringToStringView(key, key_length).value(), value);
}
// Writes a key/value pair to the buffer.
void syslog_write_key_value_double(fuchsia_syslog_log_buffer_t* buffer, const char* key,
size_t key_length, double value) {
fuchsia_syslog::WriteKeyValue(buffer, CStringToStringView(key, key_length).value(), value);
}
// Writes a key/value pair to the buffer.
void syslog_write_key_value_bool(fuchsia_syslog_log_buffer_t* buffer, const char* key,
size_t key_length, bool value) {
fuchsia_syslog::WriteKeyValue(buffer, CStringToStringView(key, key_length).value(), value);
}
// Finalizes the record. It is necessary to call this method
// before flush, and the buffer is considered "read-only"
// after calling this until BeginRecord is called
// again on the buffer to re-initialize it.
void syslog_end_record(fuchsia_syslog_log_buffer_t* buffer) { fuchsia_syslog::EndRecord(buffer); }
// Writes the LogBuffer to the socket.
bool syslog_flush_record(fuchsia_syslog_log_buffer_t* buffer) {
fuchsia_syslog::EndRecord(buffer);
return fuchsia_syslog::FlushRecord(buffer);
}
__END_CDECLS