Skip to content

Commit

Permalink
Hello World
Browse files Browse the repository at this point in the history
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
  • Loading branch information
vincenzopalazzo committed Oct 1, 2024
1 parent b5a24d1 commit a66dac0
Show file tree
Hide file tree
Showing 8 changed files with 364 additions and 3 deletions.
54 changes: 54 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Sanity Check codebase

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
check:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
node: [stable, beta, nightly]
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.node }}
override: true

- name: Run cargo check
uses: actions-rs/cargo@v1
with:
command: check

- name: Run cargo test
run: make check

lints:
name: Lints
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy

- name: Run cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
26 changes: 26 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "opentelemetry-common"
version = "0.1.0"
edition = "2021"
authors = ["Vincent <vincenzopalazzo@member.fsf.org>"]
description = "A common library for OpenTelemetry logging in Rust."
license = "GPL-2.0"
repository = "https://github.com/vincent/opentelemetry-log"
documentation = "https://docs.rs/opentelemetry-common"
homepage = "https://github.com/vincent/opentelemetry-log"
keywords = ["opentelemetry", "logging", "log", "rust"]
categories = ["development-tools"]

[dependencies]
opentelemetry = { version = "0.25", features = ["logs"] }
opentelemetry-appender-log = { version = "0.25", default-features = false }
opentelemetry_sdk = { version = "0.25", features = [ "logs", "rt-tokio" ] }
opentelemetry-otlp = { version = "0.25", features = [ "http-proto", "reqwest-client", "reqwest-rustls", "logs" ] }
opentelemetry-semantic-conventions = { version = "0.25.0" }
anyhow = "^1"
log = { version = "0.4", features = ["std"] }

[dev-dependencies]
clap = { version = "4.0.26", features = ["derive"] }
tokio = { version = "^1.29.1", features = ["rt-multi-thread", "parking_lot"] }
env_logger = "0.11.3"
5 changes: 3 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,9 @@ to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
A minimal and simple opentelemetry log adapter that allow you to
export your rust log to an opentelemetry collector.
Copyright (C) 2024 Vincenzo Palazzo <vincenzopalazzo@member.fsf.org>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
CC=cargo
FMT=fmt

OPTIONS=

default: fmt
$(CC) build
@make clippy

fmt:
$(CC) fmt --all

check:
$(CC) test --all

example:
@echo "No example for the moment"

clean:
$(CC) clean

clippy:
$(CC) clippy --all --tests

coffee:
$(CC) build --release
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,43 @@
# opentelemetry-log
A minimal and simple opentelemetry log adapter that allow you to export your rust log to an opentelemetry collector

[![CI](https://github.com/vincent/open-source/opentelemetry-log/actions/workflows/ci.yml/badge.svg)](https://github.com/vincent/open-source/opentelemetry-log/actions/workflows/ci.yml)
[![Latest Version](https://img.shields.io/crates/v/opentelemetry-log.svg)](https://crates.io/crates/opentelemetry-log)
[![License](https://img.shields.io/crates/l/opentelemetry-log.svg)](https://github.com/vincent/open-source/opentelemetry-log/blob/main/LICENSE)

A minimal and simple OpenTelemetry log adapter that allows you to export your Rust logs to an OpenTelemetry collector.

## Features

- Export Rust logs to an OpenTelemetry collector
- Minimal and simple adapter
- Easy integration with existing logging (just `log` for now) frameworks

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
opentelemetry-log = "0.1"
```

## Usage

```rust
use opentelemetry_common::Opentelemetry;

fn main() {
let mut manager = Opentelemetry::new();
manager.init_log("example", &args.level, &url)?;
// Your application code
}
```

## License

This project is licensed under the GNU General Public License. See the [LICENSE](LICENSE) file for details.

## How to Deploy OpenTelemetry with Grafana

How to inspect the logs is something that is dependent on the user, but if you are starting from scratch and
you want to learn how to work with Grafana and OpenTelemetry, I suggest starting from here: https://github.com/grafana/docker-otel-lgtm
33 changes: 33 additions & 0 deletions examples/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use clap::Parser;

use opentelemetry_common::Opentelemetry;

#[derive(Debug, Parser)]
#[clap(name = "opentelemetry.rs")]
pub struct Args {
#[clap(short, long, value_parser)]
pub url: String,
#[clap(short, long, value_parser)]
pub message: String,
#[clap(short, long)]
pub level: String,
}

// the async main is not required by our application
// but the opentelemetry app is requiring to be
// in an async context, so we use this
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
let url = args.url;

let mut manager = Opentelemetry::new();
manager.init_log("example", &args.level, &url)?;

match args.level.as_str() {
"info" => log::info!("{}", args.message),
"debug" => log::debug!("{}", args.message),
_ => anyhow::bail!("level `{}` not found", args.level),
}
Ok(())
}
96 changes: 96 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//! # OpenTelemetry Log Integration
//!
//! This crate provides integration with OpenTelemetry for logging purposes. It allows you to
//! initialize and manage loggers that are compatible with the OpenTelemetry SDK.
//!
//! ## Features
//!
//! - Initialize loggers with specific tags, levels, and exporter endpoints.
//! - Automatically manage the lifecycle of loggers, ensuring proper shutdown.
//!
//! ## Usage
//!
//! Add this crate to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! opentelemetry-log = "0.1"
//! ```
//!
//! Import and use the `Opentelemetry` struct to manage your loggers:
//!
//! ```rust
//! use opentelemetry_log::Opentelemetry;

Check failure on line 23 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (beta)

unresolved import `opentelemetry_log`

Check failure on line 23 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (nightly)

unresolved import `opentelemetry_log`
//!
//! fn main() {
//! let mut otel = Opentelemetry::new();
//! otel.init_log("my_app", "info", "http://localhost:4317").unwrap(); // Please do not unwrap in production code
//! // Your application logic here
//! }
//! ```
//!
//! ## Modules
//!
//! - `log`: Contains the log initialization logic.
//!
//! ## Structs
//!
//! - `Opentelemetry`: Main struct for managing OpenTelemetry loggers.
//!
//! ## Traits
//!
//! - `Default`: Provides a default implementation for `Opentelemetry`.
//! - `Drop`: Ensures proper shutdown of loggers when `Opentelemetry` instances are dropped.
//!
//! ## Errors
//!
//! This crate uses the `anyhow` crate for error handling. Ensure you handle errors appropriately
//! when initializing and using loggers.
pub mod log;
pub use anyhow;

use std::sync::Arc;

use opentelemetry_sdk::logs as sdklogs;

/// Main struct for managing OpenTelemetry loggers, when you init the logger
/// remember to keep this alive for all the lifetime of the application.
///
/// An example can be found in the `examples` directory.
#[derive(Debug, Clone)]
pub struct Opentelemetry {
pub(crate) logger: Option<Arc<sdklogs::LoggerProvider>>,
}

impl Default for Opentelemetry {
fn default() -> Self {
Self::new()
}
}

impl Opentelemetry {
pub fn new() -> Self {
Opentelemetry { logger: None }
}

/// Initialize a new logger with the provided tag, level, and exporter endpoint.
/// this is assuming tat your application is using `log` crate
pub fn init_log(
&mut self,
tag: &str,
level: &str,
exporter_endpoint: &str,
) -> anyhow::Result<()> {
log::init(self, tag.to_owned(), level, exporter_endpoint)?;
Ok(())
}
}

impl Drop for Opentelemetry {
fn drop(&mut self) {
let Some(Err(err)) = self.logger.as_ref().map(|log| log.shutdown()) else {
return;
};
panic!("Failed to shutdown logger: {:?}", err);
}
}
84 changes: 84 additions & 0 deletions src/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! This module provides functionality to initialize and configure a logger that exports logs
//! using OpenTelemetry. The logger can be configured with different levels and exporter endpoints.
//!
//! # Functions
//!
//! - `init`: Initializes a new logger exported with OpenTelemetry. It sets up the logger provider,
//! configures the log appender, and sets the global logger.
//! - `http_exporter`: Creates a new HTTP exporter builder for OpenTelemetry.
//!
//! # Usage
//!
//! To use this module, call the `init` function with the appropriate parameters to set up the logger.
//! The logger will then export logs to the specified endpoint using the configured protocol.
//!
//! # Example
//!
//! ```rust
//! use crate::log::init;

Check failure on line 18 in src/log.rs

View workflow job for this annotation

GitHub Actions / Build (beta)

unresolved import `crate::log`

Check failure on line 18 in src/log.rs

View workflow job for this annotation

GitHub Actions / Build (nightly)

unresolved import `crate::log`
//! use crate::Opentelemetry;

Check failure on line 19 in src/log.rs

View workflow job for this annotation

GitHub Actions / Build (beta)

unresolved import `crate::Opentelemetry`

Check failure on line 19 in src/log.rs

View workflow job for this annotation

GitHub Actions / Build (nightly)

unresolved import `crate::Opentelemetry`
//!
//! let mut manager = Opentelemetry::new();
//! let tag = "my_service".to_string();
//! let level = "info";
//! let exporter_endpoint = "http://localhost:4317";
//!
//! init(&mut manager, tag, level, exporter_endpoint).expect("Failed to initialize logger");
//! ```
//!
//! # Dependencies
//!
//! This module depends on the following crates:
//!
//! - `opentelemetry`
//! - `opentelemetry_appender_log`
//! - `opentelemetry_otlp`
//! - `opentelemetry_sdk`
//! - `log`
//! - `anyhow`
use std::str::FromStr;
use std::sync::Arc;

use opentelemetry::KeyValue;
use opentelemetry_appender_log::OpenTelemetryLogBridge;
use opentelemetry_otlp::HttpExporterBuilder;
use opentelemetry_otlp::Protocol;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::Resource;

use crate::Opentelemetry;

/// Initialize a new logger exported with open telemetry.
pub fn init(
manager: &mut Opentelemetry,
tag: String,
level: &str,
exporter_endpoint: &str,
) -> anyhow::Result<()> {
let logger_provider = opentelemetry_otlp::new_pipeline()
.logging()
.with_resource(Resource::new(vec![KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
tag,
)]))
.with_exporter(
http_exporter()
.with_protocol(Protocol::HttpBinary) //can be changed to `Protocol::HttpJson` to export in JSON format
.with_endpoint(format!("{exporter_endpoint}/v1/logs")),
)
.install_batch(opentelemetry_sdk::runtime::Tokio)?;
manager.logger = Some(Arc::new(logger_provider.clone()));

// Setup Log Appender for the log crate.
let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);

// the install method set a global provider, that we can use now
log::set_boxed_logger(Box::new(otel_log_appender)).map_err(|err| anyhow::anyhow!("{err}"))?;
let level = log::Level::from_str(level)?;
log::set_max_level(level.to_level_filter());
Ok(())
}

fn http_exporter() -> HttpExporterBuilder {
opentelemetry_otlp::new_exporter().http()
}

0 comments on commit a66dac0

Please sign in to comment.