diff --git a/api/include/opentelemetry/logs/log_record.h b/api/include/opentelemetry/logs/log_record.h deleted file mode 100644 index c663c1c9c7..0000000000 --- a/api/include/opentelemetry/logs/log_record.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include "opentelemetry/common/key_value_iterable_view.h" -#include "opentelemetry/core/timestamp.h" -#include "opentelemetry/nostd/shared_ptr.h" -#include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/trace_flags.h" -#include "opentelemetry/trace/trace_id.h" -#include "opentelemetry/version.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace logs -{ - -/* Note: using a class enum here won't allow enum values to be compared to integers, i.e. only other - * Severity enums (need an explicit cast) - * Follows the Google standard for naming: - * https://google.github.io/styleguide/cppguide.html#Enumerator_Names - */ -enum class Severity : uint8_t -{ - kTrace = 1, - kTrace2 = 2, - kTrace3 = 3, - kTrace4 = 4, - kDebug = 5, - kDebug2 = 6, - kDebug3 = 7, - kDebug4 = 8, - kInfo = 9, - kInfo2 = 10, - kInfo3 = 11, - kInfo4 = 12, - kWarn = 13, - kWarn2 = 14, - kWarn3 = 15, - kWarn4 = 16, - kError = 17, - kError2 = 18, - kError3 = 19, - kError4 = 20, - kFatal = 21, - kFatal2 = 22, - kFatal3 = 23, - kFatal4 = 24, - kDefault = kInfo // default severity is set to kInfo level, similar to what is done in ILogger -}; - -/* _nullKV is defined as a private variable that allows "resource" and - "attributes" fields to be instantiated using it as the default value */ -static common::KeyValueIterableView> _nullKV = - common::KeyValueIterableView>{{}}; - -/** - * A default Event object to be passed in log statements, - * matching the 10 fields of the Log Data Model. - * (https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/logs/data-model.md#log-and-event-record-definition) - * - */ -struct LogRecord -{ - // default fields that will be set if the user doesn't specify them - core::SystemTimestamp timestamp; // uint64 nanoseconds since Unix epoch - trace::TraceId trace_id; // byte sequence - trace::SpanId span_id; // byte sequence - trace::TraceFlags trace_flag; // byte - Severity severity; // Severity enum that combines severity_text and severity_number in the - // LogDataModel (can separate in SDK) - - // other fields that will not be set by default - nostd::string_view name; // string - nostd::string_view body; // currently a simple string, but should be changed "Any" type - common::KeyValueIterable &resource; // key/value pair list - common::KeyValueIterable &attributes; // key/value pair list - - /* Default log record if user does not overwrite this. - * TODO: find better data type to represent the type for "body" - * Future enhancement: Potentially add other constructors to take default arguments - * from the user - **/ - LogRecord() : resource(_nullKV), attributes(_nullKV) - { - // TODO: in SDK, assign a default timestamp if not specified - name = ""; - } - - /* for ease of use; user can use this function to convert a map into a KeyValueIterable for the - * resources field */ - template ::value> * = nullptr> - inline void SetResource(const T &_resource) - { - resource = common::KeyValueIterableView(_resource); - } - - /* for ease of use; user can use this function to convert a map into a KeyValueIterable for the - * attributes field */ - template ::value> * = nullptr> - inline void SetAttributes(const T &_attributes) - { - attributes = common::KeyValueIterableView(_attributes); - } -}; -} // namespace logs -OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index 5f22066460..fc486ec464 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -20,11 +20,17 @@ #include #include +#include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/key_value_iterable.h" -#include "opentelemetry/logs/log_record.h" +#include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/core/timestamp.h" +#include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/trace/trace_id.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -38,86 +44,104 @@ class Logger public: virtual ~Logger() = default; - /* Returns the name of the logger */ - // TODO: decide whether this is useful and/or should be kept, as this is not a method required in - // the specification. virtual nostd::string_view getName() = 0; - /** - * Each of the following overloaded log(...) methods + * Each of the following overloaded Log(...) methods * creates a log message with the specific parameters passed. * - * @param name the name of the log event. * @param severity the severity level of the log event. + * @param name the name of the log event. * @param message the string message of the log (perhaps support std::fmt or fmt-lib format). - * @param record the log record (object type LogRecord) that is logged. + * @param resource the resources, stored as a 2D list of key/value pairs, that are associated + * with the log event. * @param attributes the attributes, stored as a 2D list of key/value pairs, that are associated - * with this log. + * with the log event. + * @param trace_id the trace id associated with the log event. + * @param span_id the span id associate with the log event. + * @param trace_flags the trace flags associated with the log event. + * @param timestamp the timestamp the log record was created. * @throws No exceptions under any circumstances. */ - /* The below method is a logging statement that takes in a LogRecord. - * A default LogRecord that will be assigned if no parameters are passed to Logger's .log() method - * which should at minimum assign the trace_id, span_id, and timestamp + /** + * The base Log(...) method that all other Log(...) overloaded methods will eventually call, + * in order to create a log record. + * + * Note this takes in a KeyValueIterable for the resource and attributes fields. */ - virtual void log(const LogRecord &record) noexcept = 0; - - /** Overloaded methods for unstructured logging **/ - inline void log(nostd::string_view message) noexcept + virtual void Log(Severity severity, + nostd::string_view name, + nostd::string_view body, + const common::KeyValueIterable &resource, + const common::KeyValueIterable &attributes, + trace::TraceId trace_id, + trace::SpanId span_id, + trace::TraceFlags trace_flags, + core::SystemTimestamp timestamp) noexcept = 0; + + /*** Overloaded methods for KeyValueIterables ***/ + /** + * The secondary base Log(...) method that all other Log(...) overloaded methods except the one + * above will eventually call, in order to create a log record. + * + * Note this takes in template types for the resource and attributes fields. + */ + template ::value> * = nullptr, + nostd::enable_if_t::value> * = nullptr> + void Log(Severity severity, + nostd::string_view name, + nostd::string_view body, + const T &resource, + const U &attributes, + trace::TraceId trace_id, + trace::SpanId span_id, + trace::TraceFlags trace_flags, + core::SystemTimestamp timestamp) noexcept { - // Set severity to the default then call log(Severity, String message) method - log(Severity::kDefault, message); + Log(severity, name, body, common::KeyValueIterableView(resource), + common::KeyValueIterableView(attributes), trace_id, span_id, trace_flags, timestamp); } - inline void log(Severity severity, nostd::string_view message) noexcept + void Log(Severity severity, + nostd::string_view name, + nostd::string_view body, + std::initializer_list> resource, + std::initializer_list> attributes, + trace::TraceId trace_id, + trace::SpanId span_id, + trace::TraceFlags trace_flags, + core::SystemTimestamp timestamp) noexcept { - // TODO: set default timestamp later (not in API) - log(severity, message, core::SystemTimestamp(std::chrono::system_clock::now())); + return this->Log(severity, name, body, + nostd::span>{ + resource.begin(), resource.end()}, + nostd::span>{ + attributes.begin(), attributes.end()}, + trace_id, span_id, trace_flags, timestamp); } - inline void log(Severity severity, - nostd::string_view message, - core::SystemTimestamp time) noexcept - { - // creates a LogRecord object with given parameters, then calls log(LogRecord) - LogRecord r; - r.severity = severity; - r.body = message; - r.timestamp = time; + /** Wrapper methods that the user could call for convenience when logging **/ - log(r); + // Set default values for unspecified fields, then call the base Log() method + void Log(Severity severity, nostd::string_view message, core::SystemTimestamp timestamp) noexcept + { + this->Log(severity, "", message, {}, {}, {}, {}, {}, timestamp); } - /** Overloaded methods for structured logging**/ - // TODO: separate this method into separate methods since it is not useful for user to create - // empty logs - template ::value> * = nullptr> - inline void log(Severity severity = Severity::kDefault, - nostd::string_view name = "", - const T &attributes = {}) noexcept + // Set default time, and call base Log(severity, message, time) method + void Log(Severity severity, nostd::string_view message) noexcept { - log(severity, name, common::KeyValueIterableView(attributes)); + this->Log(severity, message, std::chrono::system_clock::now()); } - inline void log(Severity severity, - nostd::string_view name, - const common::KeyValueIterable &attributes) noexcept - { - // creates a LogRecord object with given parameters, then calls log(LogRecord) - LogRecord r; - r.severity = severity; - r.name = name; - r.attributes = attributes; + // Set default severity then call Log(Severity, String message) method + void Log(nostd::string_view message) noexcept { this->Log(Severity::kInfo, message); } - log(r); - } + // TODO: Add more overloaded Log(...) methods with different combiantions of parameters. - // TODO: add function aliases such as void debug(), void trace(), void info(), etc. for each + // TODO: Add function aliases such as void debug(), void trace(), void info(), etc. for each // severity level - - /** Future enhancement: templated method for objects / custom types (e.g. JSON, XML, custom - * classes, etc) **/ - // template virtual void log(T &some_obj) noexcept; }; } // namespace logs -OPENTELEMETRY_END_NAMESPACE \ No newline at end of file +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/logs/noop.h b/api/include/opentelemetry/logs/noop.h index 3184baa3c6..8a19e1a677 100644 --- a/api/include/opentelemetry/logs/noop.h +++ b/api/include/opentelemetry/logs/noop.h @@ -22,11 +22,22 @@ #include +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/context/runtime_context.h" +#include "opentelemetry/core/timestamp.h" #include "opentelemetry/logs/logger.h" #include "opentelemetry/logs/logger_provider.h" +#include "opentelemetry/logs/severity.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/version.h" + #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -39,9 +50,16 @@ namespace logs class NoopLogger final : public Logger { public: - NoopLogger() = default; - - void log(const LogRecord &record) noexcept override {} + void Log(Severity severity, + nostd::string_view name, + nostd::string_view body, + const common::KeyValueIterable &resource, + const common::KeyValueIterable &attributes, + trace::TraceId trace_id, + trace::SpanId span_id, + trace::TraceFlags trace_flags, + core::SystemTimestamp timestamp) noexcept override + {} }; /** diff --git a/api/include/opentelemetry/logs/severity.h b/api/include/opentelemetry/logs/severity.h new file mode 100644 index 0000000000..a314c9695e --- /dev/null +++ b/api/include/opentelemetry/logs/severity.h @@ -0,0 +1,71 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace logs +{ + +/** + * Severity Levels assigned to log events, based on Log Data Model, + * with the addition of kInvalid (mapped to a severity number of 0). + */ +enum class Severity : uint8_t +{ + kInvalid, + kTrace, + kTrace2, + kTrace3, + kTrace4, + kDebug, + kDebug2, + kDebug3, + kDebug4, + kInfo, + kInfo2, + kInfo3, + kInfo4, + kWarn, + kWarn2, + kWarn3, + kWarn4, + kError, + kError2, + kError3, + kError4, + kFatal, + kFatal2, + kFatal3, + kFatal4 +}; + +/** + * Mapping of the severity enum above, to a severity text string (in all caps). + * This severity text can be printed out by exporters. Capital letters follow the + * spec naming convention. + * + * Included to follow the specification's recommendation to print both + * severity number and text in each log record. + */ +const opentelemetry::nostd::string_view SeverityNumToText[25] = { + "INVALID", "TRACE", "TRACE2", "TRACE3", "TRACE4", "DEBUG", "DEBUG2", "DEBUG3", "DEBUG4", + "INFO", "INFO2", "INFO3", "INFO4", "WARN", "WARN2", "WARN3", "WARN4", "ERROR", + "ERROR2", "ERROR3", "ERROR4", "FATAL", "FATAL2", "FATAL3", "FATAL4"}; + +} // namespace logs +OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 937ece860a..d875a54c35 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -1,14 +1,13 @@ #include #include +#include "opentelemetry/core/timestamp.h" #include "opentelemetry/logs/logger.h" #include "opentelemetry/logs/provider.h" #include "opentelemetry/nostd/shared_ptr.h" -using opentelemetry::common::KeyValueIterable; using opentelemetry::logs::Logger; using opentelemetry::logs::LoggerProvider; -using opentelemetry::logs::LogRecord; using opentelemetry::logs::Provider; using opentelemetry::logs::Severity; using opentelemetry::nostd::shared_ptr; @@ -43,15 +42,22 @@ TEST(Logger, NoopLog) { auto lp = Provider::GetLoggerProvider(); auto logger = lp->GetLogger("TestLogger"); - LogRecord r; - r.name = "Noop log name"; - logger->log(r); + logger->Log("Noop log name"); } // Define a basic Logger class class TestLogger : public Logger { - void log(const LogRecord &record) noexcept override {} + void Log(Severity severity, + string_view name, + string_view body, + const opentelemetry::common::KeyValueIterable &resource, + const opentelemetry::common::KeyValueIterable &attributes, + opentelemetry::trace::TraceId trace_id, + opentelemetry::trace::SpanId span_id, + opentelemetry::trace::TraceFlags trace_flags, + opentelemetry::core::SystemTimestamp timestamp) noexcept override + {} }; // Define a basic LoggerProvider class that returns an instance of the logger class defined above diff --git a/sdk/include/opentelemetry/sdk/common/attribute_utils.h b/sdk/include/opentelemetry/sdk/common/attribute_utils.h new file mode 100644 index 0000000000..6d791d909f --- /dev/null +++ b/sdk/include/opentelemetry/sdk/common/attribute_utils.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable_view.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * A counterpart to AttributeValue that makes sure a value is owned. This + * replaces all non-owning references with owned copies. + */ +using OwnedAttributeValue = nostd::variant, + std::vector, + std::vector, + std::vector, + std::vector, + std::vector, + std::vector>; + +/** + * Creates an owned copy (OwnedAttributeValue) of a non-owning AttributeValue. + */ +struct AttributeConverter +{ + OwnedAttributeValue operator()(bool v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(int32_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(uint32_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(int64_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(uint64_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(double v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(nostd::string_view v) + { + return OwnedAttributeValue(std::string(v)); + } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) + { + return convertSpan(v); + } + + template + OwnedAttributeValue convertSpan(nostd::span vals) + { + const std::vector copy(vals.begin(), vals.end()); + return OwnedAttributeValue(std::move(copy)); + } +}; + +/** + * Class for storing attributes. + */ +class AttributeMap +{ +public: + // Contruct empty attribute map + AttributeMap(){}; + + // Contruct attribute map and populate with attributes + AttributeMap(const opentelemetry::common::KeyValueIterable &attributes) + { + attributes.ForEachKeyValue([&](nostd::string_view key, + opentelemetry::common::AttributeValue value) noexcept { + SetAttribute(key, value); + return true; + }); + } + + const std::unordered_map &GetAttributes() const noexcept + { + return attributes_; + } + + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept + { + attributes_[std::string(key)] = nostd::visit(converter_, value); + } + +private: + std::unordered_map attributes_; + AttributeConverter converter_; +}; +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/logs/exporter.h b/sdk/include/opentelemetry/sdk/logs/exporter.h index 6f44d32a28..4c01d5dabf 100644 --- a/sdk/include/opentelemetry/sdk/logs/exporter.h +++ b/sdk/include/opentelemetry/sdk/logs/exporter.h @@ -18,9 +18,9 @@ #include #include -#include "opentelemetry/logs/log_record.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/sdk/logs/processor.h" +#include "opentelemetry/sdk/logs/recordable.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -46,6 +46,17 @@ class LogExporter public: virtual ~LogExporter() = default; + /** + * Create a log recordable. This object will be used to record log data and + * will subsequently be passed to LogExporter::Export. Vendors can implement + * custom recordables or use the default LogRecord recordable provided by the + * SDK. + * @return a newly initialized Recordable object + * + * Note: This method must be callable from multiple threads. + */ + virtual std::unique_ptr MakeRecordable() noexcept = 0; + /** * Exports the batch of log records to their export destination. * This method must not be called concurrently for the same exporter instance. @@ -54,8 +65,7 @@ class LogExporter * @param records a span of unique pointers to log records * @returns an ExportResult code (whether export was success or failure) */ - virtual ExportResult Export( - const nostd::span> &records) noexcept = 0; + virtual ExportResult Export(const nostd::span> &records) noexcept = 0; /** * Marks the exporter as ShutDown and cleans up any resources as required. diff --git a/sdk/include/opentelemetry/sdk/logs/log_record.h b/sdk/include/opentelemetry/sdk/logs/log_record.h new file mode 100644 index 0000000000..4cef50ca90 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/log_record.h @@ -0,0 +1,196 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include "opentelemetry/sdk/common/attribute_utils.h" // same as traces/attribute_utils +#include "opentelemetry/sdk/logs/recordable.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +/** + * A default Recordable implemenation to be passed in log statements, + * matching the 10 fields of the Log Data Model. + * (https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/logs/data-model.md#log-and-event-record-definition) + * + */ +class LogRecord final : public Recordable +{ +private: + // Default values are set by the respective data structures' constructors for all fields, + // except the severity field, which must be set manually (an enum with no default value). + opentelemetry::logs::Severity severity_ = opentelemetry::logs::Severity::kInvalid; + common::AttributeMap resource_map_; + common::AttributeMap attributes_map_; + std::string name_; + std::string body_; // Currently a simple string, but should be changed to "Any" type + opentelemetry::trace::TraceId trace_id_; + opentelemetry::trace::SpanId span_id_; + opentelemetry::trace::TraceFlags trace_flags_; + core::SystemTimestamp timestamp_; // uint64 nanoseconds since Unix epoch + +public: + /********** Setters for each field (overrides methods from the Recordable interface) ************/ + + /** + * Set the severity for this log. + * @param severity the severity of the event + */ + void SetSeverity(opentelemetry::logs::Severity severity) noexcept override + { + severity_ = severity; + } + + /** + * Set name for this log + * @param name the name to set + */ + void SetName(nostd::string_view name) noexcept override { name_ = std::string(name); } + + /** + * Set body field for this log. + * @param message the body to set + */ + void SetBody(nostd::string_view message) noexcept override { body_ = std::string(message); } + + /** + * Set a resource for this log. + * @param name the name of the resource + * @param value the resource value + */ + void SetResource(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override + { + resource_map_.SetAttribute(key, value); + } + + /** + * Set an attribute of a log. + * @param name the name of the attribute + * @param value the attribute value + */ + + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override + { + attributes_map_.SetAttribute(key, value); + } + + /** + * Set trace id for this log. + * @param trace_id the trace id to set + */ + void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept override + { + trace_id_ = trace_id; + } + + /** + * Set span id for this log. + * @param span_id the span id to set + */ + virtual void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept override + { + span_id_ = span_id; + } + + /** + * Inject a trace_flags for this log. + * @param trace_flags the span id to set + */ + void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept override + { + trace_flags_ = trace_flags; + } + + /** + * Set the timestamp for this log. + * @param timestamp the timestamp of the event + */ + void SetTimestamp(core::SystemTimestamp timestamp) noexcept override { timestamp_ = timestamp; } + + /************************** Getters for each field ****************************/ + + /** + * Get the severity for this log + * @return the severity for this log + */ + opentelemetry::logs::Severity GetSeverity() const noexcept { return severity_; } + + /** + * Get the name of this log + * @return the name of this log + */ + std::string GetName() const noexcept { return name_; } + + /** + * Get the body of this log + * @return the body of this log + */ + std::string GetBody() const noexcept { return body_; } + + /** + * Get the resource field for this log + * @return the resource field for this log + */ + const std::unordered_map &GetResource() const noexcept + { + return resource_map_.GetAttributes(); + } + + /** + * Get the attributes for this log + * @return the attributes for this log + */ + const std::unordered_map &GetAttributes() const noexcept + { + return attributes_map_.GetAttributes(); + } + + /** + * Get the trace id for this log + * @return the trace id for this log + */ + opentelemetry::trace::TraceId GetTraceId() const noexcept { return trace_id_; } + + /** + * Get the span id for this log + * @return the span id for this log + */ + opentelemetry::trace::SpanId GetSpanId() const noexcept { return span_id_; } + + /** + * Get the trace flags for this log + * @return the trace flags for this log + */ + opentelemetry::trace::TraceFlags GetTraceFlags() const noexcept { return trace_flags_; } + + /** + * Get the timestamp for this log + * @return the timestamp for this log + */ + core::SystemTimestamp GetTimestamp() const noexcept { return timestamp_; } +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index 4b49351030..385ba3586b 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -16,15 +16,10 @@ #pragma once -#include "opentelemetry/logs/log_record.h" #include "opentelemetry/logs/logger.h" -#include "opentelemetry/nostd/shared_ptr.h" -#include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/sdk/common/atomic_shared_ptr.h" #include "opentelemetry/sdk/logs/logger_provider.h" #include "opentelemetry/sdk/logs/processor.h" -#include #include OPENTELEMETRY_BEGIN_NAMESPACE @@ -41,18 +36,40 @@ class Logger final : public opentelemetry::logs::Logger * Initialize a new logger. * @param logger_provider The logger provider that owns this logger. */ - explicit Logger(std::shared_ptr logger_provider) noexcept; + explicit Logger(opentelemetry::nostd::string_view name, + std::shared_ptr logger_provider) noexcept; /** * Writes a log record into the processor. - * @param record The record to write into the processor. - */ - void log(const opentelemetry::logs::LogRecord &record) noexcept override; + * @param severity the severity level of the log event. + * @param name the name of the log event. + * @param message the string message of the log (perhaps support std::fmt or fmt-lib format). + * @param resource the resources, stored as a 2D list of key/value pairs, that are associated + * with the log event. + * @param attributes the attributes, stored as a 2D list of key/value pairs, that are associated + * with the log event. + * @param trace_id the trace id associated with the log event. + * @param span_id the span id associate with the log event. + * @param trace_flags the trace flags associated with the log event. + * @param timestamp the timestamp the log record was created. + * @throws No exceptions under any circumstances. */ + void Log(opentelemetry::logs::Severity severity, + nostd::string_view name, + nostd::string_view body, + const opentelemetry::common::KeyValueIterable &resource, + const opentelemetry::common::KeyValueIterable &attributes, + trace::TraceId trace_id, + trace::SpanId span_id, + trace::TraceFlags trace_flags, + core::SystemTimestamp timestamp) noexcept override; private: // The logger provider of this Logger. Uses a weak_ptr to avoid cyclic dependancy issues the with // logger provider std::weak_ptr logger_provider_; + + // The name of this logger + std::string logger_name_; }; } // namespace logs diff --git a/sdk/include/opentelemetry/sdk/logs/logger_provider.h b/sdk/include/opentelemetry/sdk/logs/logger_provider.h index fdba956624..36f320d62d 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_provider.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_provider.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "opentelemetry/logs/logger_provider.h" diff --git a/sdk/include/opentelemetry/sdk/logs/processor.h b/sdk/include/opentelemetry/sdk/logs/processor.h index 7468548dcf..7e0ab6d366 100644 --- a/sdk/include/opentelemetry/sdk/logs/processor.h +++ b/sdk/include/opentelemetry/sdk/logs/processor.h @@ -18,7 +18,7 @@ #include #include -#include "opentelemetry/logs/log_record.h" +#include "opentelemetry/sdk/logs/recordable.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -34,11 +34,20 @@ class LogProcessor public: virtual ~LogProcessor() = default; + /** + * Create a log recordable. This requests a new log recordable from the + * associated exporter. + * @return a newly initialized recordable + * + * Note: This method must be callable from multiple threads. + */ + virtual std::unique_ptr MakeRecordable() noexcept = 0; + /** * OnReceive is called by the SDK once a log record has been successfully created. * @param record the log record */ - virtual void OnReceive(std::unique_ptr &&record) noexcept = 0; + virtual void OnReceive(std::unique_ptr &&record) noexcept = 0; /** * Exports all log records that have not yet been exported to the configured Exporter. diff --git a/sdk/include/opentelemetry/sdk/logs/recordable.h b/sdk/include/opentelemetry/sdk/logs/recordable.h new file mode 100644 index 0000000000..ddbd621ee4 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/recordable.h @@ -0,0 +1,92 @@ +#pragma once + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable.h" +#include "opentelemetry/core/timestamp.h" +#include "opentelemetry/logs/severity.h" +#include "opentelemetry/sdk/common/empty_attributes.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * Maintains a representation of a log in a format that can be processed by a recorder. + * + * This class is thread-compatible. + */ +class Recordable +{ +public: + virtual ~Recordable() = default; + + /** + * Set the timestamp for this log. + * @param timestamp the timestamp to set + */ + virtual void SetTimestamp(core::SystemTimestamp timestamp) noexcept = 0; + + /** + * Set the severity for this log. + * @param severity the severity of the event + */ + virtual void SetSeverity(opentelemetry::logs::Severity severity) noexcept = 0; + + /** + * Set name for this log + * @param name the name to set + */ + virtual void SetName(nostd::string_view name) noexcept = 0; + + /** + * Set body field for this log. + * @param message the body to set + */ + virtual void SetBody(nostd::string_view message) noexcept = 0; + + /** + * Set a single resource of a log record. + * @param key the name of the resource to set + * @param value the resource value to set + */ + virtual void SetResource(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept = 0; + + /** + * Set an attribute of a log. + * @param key the name of the attribute + * @param value the attribute value + */ + virtual void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept = 0; + + /** + * Set the trace id for this log. + * @param trace_id the trace id to set + */ + virtual void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept = 0; + + /** + * Set the span id for this log. + * @param span_id the span id to set + */ + virtual void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept = 0; + + /** + * Inject trace_flags for this log. + * @param trace_flags the trace flags to set + */ + virtual void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept = 0; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h b/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h index 18ed28b2cf..38a4194c16 100644 --- a/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h @@ -43,7 +43,9 @@ class SimpleLogProcessor : public LogProcessor explicit SimpleLogProcessor(std::unique_ptr &&exporter); virtual ~SimpleLogProcessor() = default; - void OnReceive(std::unique_ptr &&record) noexcept override; + std::unique_ptr MakeRecordable() noexcept override; + + void OnReceive(std::unique_ptr &&record) noexcept override; bool ForceFlush( std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index bc19f5d1c1..6e201da04e 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -13,19 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "opentelemetry/sdk/logs/logger.h" +#include "opentelemetry/sdk/logs/log_record.h" +#include "opentelemetry/trace/provider.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { namespace logs { -Logger::Logger(std::shared_ptr logger_provider) noexcept - : logger_provider_(logger_provider) +Logger::Logger(opentelemetry::nostd::string_view name, + std::shared_ptr logger_provider) noexcept + : logger_name_(std::string(name)), logger_provider_(logger_provider) {} -void Logger::log(const opentelemetry::logs::LogRecord &record) noexcept +/** + * Create and populate recordable with the log event's fields passed in. + * The timestamp, severity, traceid, spanid, and traceflags, are injected + * if the user does not specify them. + */ +void Logger::Log(opentelemetry::logs::Severity severity, + nostd::string_view name, + nostd::string_view body, + const opentelemetry::common::KeyValueIterable &resource, + const opentelemetry::common::KeyValueIterable &attributes, + opentelemetry::trace::TraceId trace_id, + opentelemetry::trace::SpanId span_id, + opentelemetry::trace::TraceFlags trace_flags, + core::SystemTimestamp timestamp) noexcept { // If this logger does not have a processor, no need to create a log record auto processor = logger_provider_.lock()->GetProcessor(); @@ -34,25 +49,73 @@ void Logger::log(const opentelemetry::logs::LogRecord &record) noexcept return; } - // TODO: Sampler logic (should include check for minSeverity) + // TODO: Sampler (should include check for minSeverity) + + auto recordable = processor->MakeRecordable(); + if (recordable == nullptr) + { + // TODO: Error diagnostics should indicate "recordable creation failed" to user + return; + } + + // Populate recordable fields + recordable->SetTimestamp(timestamp); + recordable->SetSeverity(severity); + recordable->SetName(name); + recordable->SetBody(body); + + resource.ForEachKeyValue([&](nostd::string_view key, + opentelemetry::common::AttributeValue value) noexcept { + recordable->SetResource(key, value); + return true; + }); + + attributes.ForEachKeyValue([&](nostd::string_view key, + opentelemetry::common::AttributeValue value) noexcept { + recordable->SetAttribute(key, value); + return true; + }); - /** - * Convert the LogRecord to the heap first before sending to processor. - * TODO: Change the API log(LogRecord) function to log(*LogRecord) so the following line - * converting record a heap variable can be removed - */ - auto record_pointer = - std::unique_ptr(new opentelemetry::logs::LogRecord(record)); + // Inject trace_id/span_id/trace_flags if none is set by user + auto provider = opentelemetry::trace::Provider::GetTracerProvider(); + auto tracer = provider->GetTracer(logger_name_); + auto span_context = tracer->GetCurrentSpan()->GetContext(); - // TODO: Do not want to overwrite user-set timestamp if there already is one - - // add a flag in the API to check if timestamp is set by user already before setting timestamp + // Leave these fields in the recordable empty if neither the passed in values + // nor the context values are valid (e.g. the application is not using traces) - // Inject timestamp if none is set - record_pointer->timestamp = core::SystemTimestamp(std::chrono::system_clock::now()); - // TODO: inject traceid/spanid later + // Traceid + if (trace_id.IsValid()) + { + recordable->SetTraceId(trace_id); + } + else if (span_context.trace_id().IsValid()) + { + recordable->SetTraceId(span_context.trace_id()); + } + + // Spanid + if (span_id.IsValid()) + { + recordable->SetSpanId(span_id); + } + else if (span_context.span_id().IsValid()) + { + recordable->SetSpanId(span_id); + } + + // Traceflags + if (trace_flags.IsSampled()) + { + recordable->SetTraceFlags(trace_flags); + } + else if (span_context.trace_flags().IsSampled()) + { + recordable->SetTraceFlags(span_context.trace_flags()); + } // Send the log record to the processor - processor->OnReceive(std::move(record_pointer)); + processor->OnReceive(std::move(recordable)); } } // namespace logs diff --git a/sdk/src/logs/logger_provider.cc b/sdk/src/logs/logger_provider.cc index b040f9baab..6c978c3472 100644 --- a/sdk/src/logs/logger_provider.cc +++ b/sdk/src/logs/logger_provider.cc @@ -54,7 +54,7 @@ opentelemetry::nostd::shared_ptr LoggerProvider::Ge // If no logger with that name exists yet, create it and add it to the map of loggers opentelemetry::nostd::shared_ptr logger( - new Logger(this->shared_from_this())); + new Logger(name, this->shared_from_this())); loggers_[name.data()] = logger; return logger; } diff --git a/sdk/src/logs/simple_log_processor.cc b/sdk/src/logs/simple_log_processor.cc index d88fc9eb92..d6b8dda477 100644 --- a/sdk/src/logs/simple_log_processor.cc +++ b/sdk/src/logs/simple_log_processor.cc @@ -32,14 +32,18 @@ SimpleLogProcessor::SimpleLogProcessor(std::unique_ptr &&exporter) : exporter_(std::move(exporter)) {} +std::unique_ptr SimpleLogProcessor::MakeRecordable() noexcept +{ + return exporter_->MakeRecordable(); +} + /** * Batches the log record it receives in a batch of 1 and immediately sends it * to the configured exporter */ -void SimpleLogProcessor::OnReceive( - std::unique_ptr &&record) noexcept +void SimpleLogProcessor::OnReceive(std::unique_ptr &&record) noexcept { - nostd::span> batch(&record, 1); + nostd::span> batch(&record, 1); // Get lock to ensure Export() is never called concurrently const std::lock_guard locked(lock_); diff --git a/sdk/test/logs/BUILD b/sdk/test/logs/BUILD index ccbbfd8c78..9e12e08ab9 100644 --- a/sdk/test/logs/BUILD +++ b/sdk/test/logs/BUILD @@ -31,3 +31,14 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "log_record_test", + srcs = [ + "log_record_test.cc", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/logs/CMakeLists.txt b/sdk/test/logs/CMakeLists.txt index 024c42c45a..f59c6a1926 100644 --- a/sdk/test/logs/CMakeLists.txt +++ b/sdk/test/logs/CMakeLists.txt @@ -1,5 +1,5 @@ foreach(testname logger_provider_sdk_test logger_sdk_test - simple_log_processor_test) + simple_log_processor_test log_record_test) add_executable(${testname} "${testname}.cc") target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_logs) diff --git a/sdk/test/logs/log_record_test.cc b/sdk/test/logs/log_record_test.cc new file mode 100644 index 0000000000..774e726c54 --- /dev/null +++ b/sdk/test/logs/log_record_test.cc @@ -0,0 +1,59 @@ +#include "opentelemetry/sdk/logs/log_record.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_id.h" + +#include + +using opentelemetry::sdk::logs::LogRecord; + +// Test what a default LogRecord with no fields set holds +TEST(LogRecord, GetDefaultValues) +{ + opentelemetry::trace::TraceId zero_trace_id; + opentelemetry::trace::SpanId zero_span_id; + opentelemetry::trace::TraceFlags zero_trace_flags; + LogRecord record; + + ASSERT_EQ(record.GetSeverity(), opentelemetry::logs::Severity::kInvalid); + ASSERT_EQ(record.GetName(), ""); + ASSERT_EQ(record.GetBody(), ""); + ASSERT_EQ(record.GetResource().size(), 0); + ASSERT_EQ(record.GetAttributes().size(), 0); + ASSERT_EQ(record.GetTraceId(), zero_trace_id); + ASSERT_EQ(record.GetSpanId(), zero_span_id); + ASSERT_EQ(record.GetTraceFlags(), zero_trace_flags); + ASSERT_EQ(record.GetTimestamp().time_since_epoch(), std::chrono::nanoseconds(0)); +} + +// Test LogRecord fields are properly set and get +TEST(LogRecord, SetAndGet) +{ + opentelemetry::trace::TraceId trace_id; + opentelemetry::trace::SpanId span_id; + opentelemetry::trace::TraceFlags trace_flags; + opentelemetry::core::SystemTimestamp now(std::chrono::system_clock::now()); + + // Set all fields of the LogRecord + LogRecord record; + record.SetSeverity(opentelemetry::logs::Severity::kInvalid); + record.SetName("Log name"); + record.SetBody("Message"); + record.SetResource("res1", (bool)true); + record.SetAttribute("attr1", (int64_t)314159); + record.SetTraceId(trace_id); + record.SetSpanId(span_id); + record.SetTraceFlags(trace_flags); + record.SetTimestamp(now); + + // Test that all fields match what was set + ASSERT_EQ(record.GetSeverity(), opentelemetry::logs::Severity::kInvalid); + ASSERT_EQ(record.GetName(), "Log name"); + ASSERT_EQ(record.GetBody(), "Message"); + ASSERT_EQ(opentelemetry::nostd::get(record.GetResource().at("res1")), 1); + ASSERT_EQ(opentelemetry::nostd::get(record.GetAttributes().at("attr1")), 314159); + ASSERT_EQ(record.GetTraceId(), trace_id); + ASSERT_EQ(record.GetSpanId(), span_id); + ASSERT_EQ(record.GetTraceFlags(), trace_flags); + ASSERT_EQ(record.GetTimestamp().time_since_epoch(), now.time_since_epoch()); +} diff --git a/sdk/test/logs/logger_provider_sdk_test.cc b/sdk/test/logs/logger_provider_sdk_test.cc index 02e7c47275..b028afdef9 100644 --- a/sdk/test/logs/logger_provider_sdk_test.cc +++ b/sdk/test/logs/logger_provider_sdk_test.cc @@ -16,6 +16,7 @@ #include "opentelemetry/logs/provider.h" #include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/sdk/logs/log_record.h" #include "opentelemetry/sdk/logs/logger.h" #include "opentelemetry/sdk/logs/logger_provider.h" @@ -69,7 +70,12 @@ TEST(LoggerProviderSDK, LoggerProviderLoggerArguments) class DummyProcessor : public LogProcessor { - void OnReceive(std::unique_ptr &&record) noexcept {} + std::unique_ptr MakeRecordable() noexcept + { + return std::unique_ptr(new LogRecord); + } + + void OnReceive(std::unique_ptr &&record) noexcept {} bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept { return true; diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index d4825d0032..e1993f15ec 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "opentelemetry/sdk/logs/log_record.h" #include "opentelemetry/sdk/logs/logger.h" #include @@ -22,7 +23,7 @@ using namespace opentelemetry::sdk::logs; TEST(LoggerSDK, LogToNullProcessor) { - // Confirm Logger::log() does not have undefined behavior + // Confirm Logger::Log() does not have undefined behavior // even when there is no processor set // since it calls Processor::OnReceive() @@ -30,14 +31,37 @@ TEST(LoggerSDK, LogToNullProcessor) auto logger = lp->GetLogger("logger"); // Log a sample log record to a nullptr processor - opentelemetry::logs::LogRecord r; - r.name = "Test log"; - logger->log(r); + logger->Log("Test log"); } -class DummyProcessor : public LogProcessor +class MockProcessor final : public LogProcessor { - void OnReceive(std::unique_ptr &&record) noexcept {} +private: + std::shared_ptr record_received_; + +public: + // A processor used for testing that keeps a track of the recordable it received + explicit MockProcessor(std::shared_ptr record_received) noexcept + : record_received_(record_received) + {} + + std::unique_ptr MakeRecordable() noexcept + { + return std::unique_ptr(new LogRecord); + } + // OnReceive stores the record it receives into the shared_ptr recordable passed into its + // constructor + void OnReceive(std::unique_ptr &&record) noexcept + { + // Cast the recordable received into a concrete LogRecord type + auto copy = std::shared_ptr(static_cast(record.release())); + + // Copy over the received log record's name, body, timestamp fields over to the recordable + // passed in the constructor + record_received_->SetSeverity(copy->GetSeverity()); + record_received_->SetBody(copy->GetBody()); + record_received_->SetTimestamp(copy->GetTimestamp()); + } bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept { return true; @@ -62,16 +86,16 @@ TEST(LoggerSDK, LogToAProcessor) ASSERT_EQ(logger, logger2); // Set a processor for the LoggerProvider - std::shared_ptr processor = std::shared_ptr(new DummyProcessor()); + auto shared_recordable = std::shared_ptr(new LogRecord()); + auto processor = std::shared_ptr(new MockProcessor(shared_recordable)); lp->SetProcessor(processor); ASSERT_EQ(processor, lp->GetProcessor()); - // Should later introduce a way to assert that - // the logger's processor is the same as "proc" - // and that the logger's processor is the same as lp's processor + // Check that the recordable created by the Log() statement is set properly + opentelemetry::core::SystemTimestamp now(std::chrono::system_clock::now()); + logger->Log(opentelemetry::logs::Severity::kWarn, "Message", now); - // Log a sample log record to the processor - opentelemetry::logs::LogRecord r; - r.name = "Test log"; - logger->log(r); + ASSERT_EQ(shared_recordable->GetSeverity(), opentelemetry::logs::Severity::kWarn); + ASSERT_EQ(shared_recordable->GetBody(), "Message"); + ASSERT_EQ(shared_recordable->GetTimestamp().time_since_epoch(), now.time_since_epoch()); } diff --git a/sdk/test/logs/simple_log_processor_test.cc b/sdk/test/logs/simple_log_processor_test.cc index 80c83183d3..c653401f47 100644 --- a/sdk/test/logs/simple_log_processor_test.cc +++ b/sdk/test/logs/simple_log_processor_test.cc @@ -1,5 +1,7 @@ #include "opentelemetry/sdk/logs/simple_log_processor.h" +#include "opentelemetry/nostd/span.h" #include "opentelemetry/sdk/logs/exporter.h" +#include "opentelemetry/sdk/logs/log_record.h" #include @@ -7,7 +9,6 @@ #include using namespace opentelemetry::sdk::logs; -using opentelemetry::logs::LogRecord; /* * A test exporter that can return a vector of all the records it has received, @@ -17,21 +18,31 @@ class TestExporter final : public LogExporter { public: TestExporter(int *shutdown_counter, - std::shared_ptr> logs_received, + std::shared_ptr>> logs_received, size_t *batch_size_received) : shutdown_counter_(shutdown_counter), logs_received_(logs_received), batch_size_received(batch_size_received) {} + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new LogRecord()); + } + // Stores the names of the log records this exporter receives to an internal list ExportResult Export( - const opentelemetry::nostd::span> &records) noexcept override + const opentelemetry::nostd::span> &records) noexcept override { *batch_size_received = records.size(); for (auto &record : records) { - logs_received_->push_back(record->name.data()); + auto log_record = std::unique_ptr(static_cast(record.release())); + + if (log_record != nullptr) + { + logs_received_->push_back(std::move(log_record)); + } } return ExportResult::kSuccess; } @@ -45,7 +56,7 @@ class TestExporter final : public LogExporter private: int *shutdown_counter_; - std::shared_ptr> logs_received_; + std::shared_ptr>> logs_received_; size_t *batch_size_received; }; @@ -54,7 +65,8 @@ class TestExporter final : public LogExporter TEST(SimpleLogProcessorTest, SendReceivedLogsToExporter) { // Create a simple processor with a TestExporter attached - std::shared_ptr> logs_received(new std::vector); + std::shared_ptr>> logs_received( + new std::vector>); size_t batch_size_received = 0; std::unique_ptr exporter( @@ -66,12 +78,9 @@ TEST(SimpleLogProcessorTest, SendReceivedLogsToExporter) const int num_logs = 5; for (int i = 0; i < num_logs; i++) { - auto record = std::unique_ptr(new LogRecord()); - std::string s("Log name"); - s += std::to_string(i); - record->name = s; - - processor.OnReceive(std::move(record)); + auto recordable = processor.MakeRecordable(); + recordable->SetName("Log"); + processor.OnReceive(std::move(recordable)); // Verify that the batch of 1 log record sent by processor matches what exporter received EXPECT_EQ(1, batch_size_received); @@ -81,9 +90,7 @@ TEST(SimpleLogProcessorTest, SendReceivedLogsToExporter) EXPECT_EQ(logs_received->size(), num_logs); for (int i = 0; i < num_logs; i++) { - std::string s("Log name"); - s += std::to_string(i); - EXPECT_EQ(s, logs_received->at(i)); + EXPECT_EQ("Log", logs_received->at(i)->GetName()); } } @@ -115,8 +122,13 @@ class FailShutDownExporter final : public LogExporter public: FailShutDownExporter() {} + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new LogRecord()); + } + ExportResult Export( - const opentelemetry::nostd::span> &records) noexcept override + const opentelemetry::nostd::span> &records) noexcept override { return ExportResult::kSuccess; }