Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation of the logging SDK spec #788

Merged
merged 52 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
7b6ed21
Logging SDK.
vibhavp Mar 13, 2022
56edfb5
Add From implementations for Any.
vibhavp Mar 13, 2022
54a41da
Fix docs links.
vibhavp Mar 13, 2022
6d0875c
Add Into<Any> impls for f64 and f32.
vibhavp Mar 13, 2022
1a2b532
Support converting serde_json::Value to Any.
vibhavp Mar 13, 2022
c7631dd
Fix docs link.
vibhavp Mar 13, 2022
8c23c09
Remove unused dependency.
vibhavp Mar 14, 2022
085b598
Add LogRecordBuilder, documentation for Severity.
vibhavp Mar 14, 2022
5c8539e
Add LogRecordBuilder::new().
vibhavp Mar 14, 2022
1b9f870
with_body: Remove unneeded generic parameter.
vibhavp Mar 14, 2022
4bb4216
Remove unneeded generic parameters.
vibhavp Mar 14, 2022
4ebe135
Enforce LogProcessor is Send + Sync.
vibhavp Mar 15, 2022
d0016f1
export: Use the correct variables.
vibhavp Mar 15, 2022
10c69c2
LogEmitterProvider: Enable Clone. Add shutdown, try_shutdown.
vibhavp Mar 15, 2022
2f0ae2c
Remove From implementation for serde_json values.
vibhavp Mar 15, 2022
ecf13cf
Fix typo.
vibhavp Mar 19, 2022
5c1b0b7
Add Default impl for LogRecordBuilder.
vibhavp Mar 22, 2022
092376f
Update to work with opentelemetry-proto v0.14.0.
vibhavp Mar 25, 2022
3eb34cd
Remove tests.
vibhavp Mar 28, 2022
0e0bbbf
Avoid using wildcard imports.
vibhavp Apr 1, 2022
22f5ab5
Rename feature/module "log" to "logs".
vibhavp May 7, 2022
d3b7b15
Implement Drop for LogEmitterProvider.
vibhavp May 7, 2022
aeb53ff
Use std::convert::identity.
vibhavp May 7, 2022
0593aa5
Use opentelemetry-log-exporter as the thread name.
vibhavp May 7, 2022
fec1cdf
Use the correct module name.
vibhavp May 7, 2022
e0fae82
Remove From<Severity> impls for SeverityNumber.
vibhavp May 7, 2022
4777062
log_emitter: Set emitter version as None.
vibhavp May 7, 2022
a8e64d6
Rename attributes_to_keyvalue to attributes_to_keyv_alue
vibhavp May 7, 2022
fbe8c4e
Update logs
vibhavp Apr 24, 2023
d85295d
Fix typos in feature names.
vibhavp Apr 24, 2023
b7dbee5
Add logs protobuf files to GRPCIO_PROTO_FILES.
vibhavp Apr 24, 2023
93bca4f
Update to opentelemetry-proto 0.19.0.
vibhavp Apr 24, 2023
3a8f49a
Merge remote-tracking branch 'upstream/main'
vibhavp Apr 24, 2023
e20e697
Update crates/modules names.
vibhavp Apr 24, 2023
e77388a
Remove incorrect exporter example in docs.
vibhavp Apr 24, 2023
0d88043
Move stdout logs exporter to the opentelemetry-stdout crate.
vibhavp Apr 26, 2023
3a34dea
Store resource using Cow instead of Arc.
vibhavp Apr 27, 2023
5f75512
Add From<Cow<str>> implementation for Key.
vibhavp Apr 27, 2023
1656a91
Update logging SDK.
vibhavp Apr 27, 2023
a9b41aa
Add ordered-float dependency.
vibhavp Apr 27, 2023
26b2b1e
Rewrite LogsExporter.
vibhavp Apr 27, 2023
283f932
Move LogRecord to api crate, simplify resources.
vibhavp Apr 29, 2023
322da85
Add API traits for logs.
vibhavp Apr 30, 2023
93f5acd
Add api trait impls.
vibhavp Apr 30, 2023
070b496
Add no-op impl for Logger and LoggerProvider.
vibhavp Apr 30, 2023
173fc3d
Use api traits.
vibhavp Apr 30, 2023
b2fc857
Add global logger/loggerproviders.
vibhavp Apr 30, 2023
47731ca
Add include_trace_context, make the component name param ergonomic.
vibhavp May 6, 2023
a617fd5
Update docs.
vibhavp May 7, 2023
ad676ac
fix: lint
TommyCpp May 10, 2023
5efc75d
logs: Rename Any to AnyValue.
vibhavp May 13, 2023
59469cb
Address docs and lint issues.
vibhavp May 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions opentelemetry-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ default = ["trace"]
trace = ["pin-project-lite"]
metrics = ["fnv"]
testing = ["trace"]
logs = []
10 changes: 10 additions & 0 deletions opentelemetry-api/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ impl From<Arc<str>> for Key {
}
}

impl From<Cow<'static, str>> for Key {
/// Convert a `Cow<'static, str>` to a `Key`
fn from(string: Cow<'static, str>) -> Self {
match string {
Cow::Borrowed(s) => Key(OtelString::Static(s)),
Cow::Owned(s) => Key(OtelString::Owned(s)),
}
}
}

impl fmt::Debug for Key {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
Expand Down
12 changes: 12 additions & 0 deletions opentelemetry-api/src/global/error_handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::sync::PoisonError;
vibhavp marked this conversation as resolved.
Show resolved Hide resolved
use std::sync::RwLock;

#[cfg(feature = "logs")]
use crate::logs::LogError;
#[cfg(feature = "metrics")]
use crate::metrics::MetricsError;
#[cfg(feature = "trace")]
Expand All @@ -23,6 +25,13 @@ pub enum Error {
#[error(transparent)]
/// An issue raised by the metrics module.
Metric(#[from] MetricsError),

#[cfg(feature = "logs")]
#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
#[error(transparent)]
/// Failed to export logs.
Log(#[from] LogError),

#[error("{0}")]
/// Other types of failures not covered by the variants above.
Other(String),
Expand All @@ -49,6 +58,9 @@ pub fn handle_error<T: Into<Error>>(err: T) {
#[cfg(feature = "trace")]
#[cfg_attr(docsrs, doc(cfg(feature = "trace")))]
Error::Trace(err) => eprintln!("OpenTelemetry trace error occurred. {}", err),
#[cfg(feature = "logs")]
#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
Error::Log(err) => eprintln!("OpenTelemetry log error occurred. {}", err),
Error::Other(err_msg) => eprintln!("OpenTelemetry error occurred. {}", err_msg),
},
}
Expand Down
158 changes: 158 additions & 0 deletions opentelemetry-api/src/global/logs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use std::{
borrow::Cow,
fmt, mem,
sync::{Arc, RwLock},
};

use once_cell::sync::Lazy;

use crate::{
logs::{Logger, LoggerProvider, NoopLoggerProvider},
KeyValue,
};

/// Allows a specific [`LoggerProvider`] to be used generically, by mirroring
/// the interface, and boxing the returned types.
///
/// [`LoggerProvider`]: crate::logs::LoggerProvider.
pub trait ObjectSafeLoggerProvider {
/// Creates a versioned named [`Logger`] instance that is a trait object
/// through the underlying [`LoggerProvider`].
///
/// [`Logger`]: crate::logs::Logger
/// [`LoggerProvider`]: crate::logs::LoggerProvider
fn versioned_logger_boxed(
&self,
name: Cow<'static, str>,
version: Option<Cow<'static, str>>,
schema_url: Option<Cow<'static, str>>,
attributes: Option<Vec<KeyValue>>,
include_trace_context: bool,
) -> Box<dyn Logger + Send + Sync + 'static>;
}

impl<L, P> ObjectSafeLoggerProvider for P
where
L: Logger + Send + Sync + 'static,
P: LoggerProvider<Logger = L>,
{
fn versioned_logger_boxed(
&self,
name: Cow<'static, str>,
version: Option<Cow<'static, str>>,
schema_url: Option<Cow<'static, str>>,
attributes: Option<Vec<KeyValue>>,
include_trace_context: bool,
) -> Box<dyn Logger + Send + Sync + 'static> {
Box::new(self.versioned_logger(
name,
version,
schema_url,
attributes,
include_trace_context,
))
}
}

pub struct BoxedLogger(Box<dyn Logger + Send + Sync + 'static>);

impl fmt::Debug for BoxedLogger {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("BoxedLogger")
}
}

impl Logger for BoxedLogger {
fn emit(&self, record: crate::logs::LogRecord) {
self.0.emit(record)
}
}

#[derive(Clone)]
/// Represents the globally configured [`LoggerProvider`] instance.
pub struct GlobalLoggerProvider {
provider: Arc<dyn ObjectSafeLoggerProvider + Send + Sync>,
}

impl fmt::Debug for GlobalLoggerProvider {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GlobalLoggerProvider")
}
}

impl GlobalLoggerProvider {
fn new<
L: Logger + Send + Sync + 'static,
P: LoggerProvider<Logger = L> + Send + Sync + 'static,
>(
provider: P,
) -> Self {
GlobalLoggerProvider {
provider: Arc::new(provider),
}
}
}

impl LoggerProvider for GlobalLoggerProvider {
type Logger = BoxedLogger;

fn versioned_logger(
&self,
name: impl Into<Cow<'static, str>>,
version: Option<Cow<'static, str>>,
schema_url: Option<Cow<'static, str>>,
attributes: Option<Vec<KeyValue>>,
include_trace_context: bool,
) -> Self::Logger {
BoxedLogger(self.provider.versioned_logger_boxed(
name.into(),
version,
schema_url,
attributes,
include_trace_context,
))
}
}

static GLOBAL_LOGGER_PROVIDER: Lazy<RwLock<GlobalLoggerProvider>> =
Lazy::new(|| RwLock::new(GlobalLoggerProvider::new(NoopLoggerProvider::new())));

/// Returns an instance of the currently configured global [`LoggerProvider`]
/// through [`GlobalLoggerProvider`].
///
/// [`LoggerProvider`]: crate::logs::LoggerProvider
pub fn logger_provider() -> GlobalLoggerProvider {
GLOBAL_LOGGER_PROVIDER
.read()
.expect("GLOBAL_LOGGER_PROVIDER RwLock poisoned")
.clone()
}

/// Creates a named instance of [`Logger`] via the configured
/// [`GlobalLoggerProvider`].
///
/// If `name` is an empty string, the provider will use a default name.
///
/// [`Logger`]: crate::logs::Logger
pub fn logger(name: Cow<'static, str>) -> BoxedLogger {
logger_provider().logger(name)
}

/// Sets the given [`LoggerProvider`] instance as the current global provider,
/// returning the [`LoggerProvider`] instance that was previously set as global
/// provider.
pub fn set_logger_provider<L, P>(new_provider: P) -> GlobalLoggerProvider
where
L: Logger + Send + Sync + 'static,
P: LoggerProvider<Logger = L> + Send + Sync + 'static,
{
let mut provider = GLOBAL_LOGGER_PROVIDER
.write()
.expect("GLOBAL_LOGGER_PROVIDER RwLock poisoned");
mem::replace(&mut *provider, GlobalLoggerProvider::new(new_provider))
}

/// Shut down the current global [`LoggerProvider`].
pub fn shutdown_logger_provider() {
let _ = set_logger_provider(NoopLoggerProvider::new());
}
8 changes: 8 additions & 0 deletions opentelemetry-api/src/global/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@
//! [`set_meter_provider`]: crate::global::set_meter_provider

mod error_handler;
#[cfg(feature = "logs")]
mod logs;
#[cfg(feature = "metrics")]
mod metrics;
#[cfg(feature = "trace")]
Expand All @@ -150,6 +152,12 @@ mod propagation;
mod trace;

pub use error_handler::{handle_error, set_error_handler, Error};
#[cfg(feature = "logs")]
#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
pub use logs::{
logger, logger_provider, set_logger_provider, shutdown_logger_provider, GlobalLoggerProvider,
ObjectSafeLoggerProvider,
};
#[cfg(feature = "metrics")]
#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
pub use metrics::{
Expand Down
4 changes: 4 additions & 0 deletions opentelemetry-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ pub mod propagation;
#[cfg_attr(docsrs, doc(cfg(feature = "trace")))]
pub mod trace;

#[cfg(feature = "logs")]
#[cfg_attr(docsrs, doc(cfg(feature = "logs")))]
pub mod logs;

#[doc(hidden)]
#[cfg(any(feature = "metrics", feature = "trace"))]
pub mod time {
Expand Down
52 changes: 52 additions & 0 deletions opentelemetry-api/src/logs/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::borrow::Cow;

use crate::{logs::LogRecord, KeyValue};

/// The interface for emitting [`LogRecord`]s.
pub trait Logger {
/// Emit a [`LogRecord`]. If this `Logger` was created with
/// `include_trace_context` set to `true`, the logger will set the record's
/// [`TraceContext`] to the active trace context, using the current thread's
/// [`Context`].
///
/// [`Context`]: crate::Context
/// [`TraceContext`]: crate::logs::TraceContext
fn emit(&self, record: LogRecord);
}

/// Interfaces that can create [`Logger`] instances.
pub trait LoggerProvider {
/// The [`Logger`] type that this provider will return.
type Logger: Logger;

/// Returns a new versioned logger with a given name.
///
/// The `name` should be the application name or the name of the library
/// providing instrumentation. If the name is empty, then an
/// implementation-defined default name may be used instead.
///
/// If `include_trace_context` is `true`, the newly created [`Logger`]
/// should set the [`TraceContext`] associated with a record to the
/// current thread's active trace context, using [`Context`].
///
/// [`Context`]: crate::Context
/// [`TraceContext`]: crate::logs::TraceContext

fn versioned_logger(
&self,
name: impl Into<Cow<'static, str>>,
version: Option<Cow<'static, str>>,
schema_url: Option<Cow<'static, str>>,
attributes: Option<Vec<KeyValue>>,
include_trace_context: bool,
) -> Self::Logger;

/// Returns a new logger with the given name.
///
/// The `name` should be the application name or the name of the library
/// providing instrumentation. If the name is empty, then an
/// implementation-defined default name may be used instead.
fn logger(&self, name: impl Into<Cow<'static, str>>) -> Self::Logger {
self.versioned_logger(name, None, None, None, true)
}
}
72 changes: 72 additions & 0 deletions opentelemetry-api/src/logs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! # OpenTelemetry Logs API

use crate::ExportError;
use futures_channel::{mpsc::TrySendError, oneshot::Canceled};
use std::time::Duration;
use thiserror::Error;

mod logger;
mod noop;
mod record;

pub use logger::{Logger, LoggerProvider};
pub use noop::NoopLoggerProvider;
pub use record::{AnyValue, LogRecord, LogRecordBuilder, Severity, TraceContext};

/// Describe the result of operations in log SDK.
pub type LogResult<T> = Result<T, LogError>;

#[derive(Error, Debug)]
#[non_exhaustive]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More a comment for when we look to stabilize, but non-exhaustive is a double edged sword because while adding a new enum value is considered breaking without it because code will fail to compile. It might be worse to have people think they covered all errors and then find out at runtime.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, better to fail early at compile time. Probably we can remove non-exhaustive attribute before releasing stable version of logs.

/// Errors returned by the log SDK.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be in the SDK only. Its probably a mistake that was already in traces api. See #1042

pub enum LogError {
/// Export failed with the error returned by the exporter.
#[error("Exporter {} encountered the following errors: {0}", .0.exporter_name())]
ExportFailed(Box<dyn ExportError>),

/// Export failed to finish after certain period and processor stopped the export.
#[error("Exporter timed out after {} seconds", .0.as_secs())]
ExportTimedOut(Duration),

/// Other errors propagated from log SDK that weren't covered above.
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
}

impl<T> From<T> for LogError
where
T: ExportError,
{
fn from(err: T) -> Self {
LogError::ExportFailed(Box::new(err))
}
}

impl<T> From<TrySendError<T>> for LogError {
fn from(err: TrySendError<T>) -> Self {
LogError::Other(Box::new(err.into_send_error()))
}
}

impl From<Canceled> for LogError {
fn from(err: Canceled) -> Self {
LogError::Other(Box::new(err))
}
}

impl From<String> for LogError {
fn from(err_msg: String) -> Self {
LogError::Other(Box::new(Custom(err_msg)))
}
}

impl From<&'static str> for LogError {
fn from(err_msg: &'static str) -> Self {
LogError::Other(Box::new(Custom(err_msg.into())))
}
}

/// Wrap type for string
#[derive(Error, Debug)]
#[error("{0}")]
struct Custom(String);
Loading