Skip to content

Commit 774636a

Browse files
authored
Add OpenTelemetry logs exporter targeting user_events on Linux (#286)
1 parent b666b2e commit 774636a

File tree

19 files changed

+1075
-0
lines changed

19 files changed

+1075
-0
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "exporters/geneva-trace/third_party/opentelemetry-cpp"]
22
path = exporters/geneva-trace/third_party/opentelemetry-cpp
33
url = https://github.com/open-telemetry/opentelemetry-cpp
4+
[submodule "exporters/user_events/third_party/LinuxTracepoints"]
5+
path = exporters/user_events/third_party/LinuxTracepoints
6+
url = https://github.com/microsoft/LinuxTracepoints
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# See Clang docs: http://clang.llvm.org/docs/ClangFormatStyleOptions.html
5+
BasedOnStyle: Chromium
6+
7+
# Allow double brackets such as std::vector<std::vector<int>>.
8+
Standard: Cpp11
9+
10+
# Indent 2 spaces at a time.
11+
IndentWidth: 2
12+
13+
# Keep lines under 100 columns long.
14+
ColumnLimit: 100
15+
16+
# Always break before braces
17+
BreakBeforeBraces: Custom
18+
BraceWrapping:
19+
# TODO(lujc) wait for clang-format-9 support in Chromium tools
20+
# AfterCaseLabel: true
21+
AfterClass: true
22+
AfterControlStatement: true
23+
AfterEnum: true
24+
AfterFunction: true
25+
AfterNamespace: true
26+
AfterStruct: true
27+
AfterUnion: true
28+
BeforeCatch: true
29+
BeforeElse: true
30+
IndentBraces: false
31+
SplitEmptyFunction: false
32+
SplitEmptyRecord: false
33+
SplitEmptyNamespace: false
34+
35+
# Keeps extern "C" blocks unindented.
36+
AfterExternBlock: false
37+
38+
# Indent case labels.
39+
IndentCaseLabels: true
40+
41+
# Right-align pointers and references
42+
PointerAlignment: Right
43+
44+
# ANGLE likes to align things as much as possible.
45+
AlignOperands: true
46+
AlignConsecutiveAssignments: true
47+
48+
# Use 2 space negative offset for access modifiers
49+
AccessModifierOffset: -2
50+
51+
# TODO(jmadill): Decide if we want this on. Doesn't have an "all or none" mode.
52+
AllowShortCaseLabelsOnASingleLine: false
53+
54+
# Useful for spacing out functions in classes
55+
KeepEmptyLinesAtTheStartOfBlocks: true
56+
57+
# Indent nested PP directives.
58+
IndentPPDirectives: AfterHash
59+
60+
# Include blocks style
61+
IncludeBlocks: Preserve
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# If comment markup is enabled, don't reflow the first comment block in
5+
# eachlistfile. Use this to preserve formatting of your
6+
# copyright/licensestatements.
7+
first_comment_is_literal = True

exporters/user_events/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
out/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"configurations": [
3+
{
4+
"name": "otel_log",
5+
"defines": [
6+
"ENABLE_LOGS_PREVIEW"
7+
]
8+
}
9+
],
10+
"version": 4
11+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
cmake_minimum_required(VERSION 3.12)
2+
3+
set(CMAKE_CXX_STANDARD 17)
4+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
5+
6+
if(WIN32)
7+
message(FATAL_ERROR "user_events is Linux only for now")
8+
endif()
9+
10+
option(BUILD_EXAMPLE "Build example" ON)
11+
option(BUILD_TESTING "Build tests" ON)
12+
13+
set(MAIN_PROJECT OFF)
14+
15+
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
16+
project(opentelemetry-userevents-logs)
17+
set(MAIN_PROJECT ON)
18+
endif()
19+
20+
add_definitions(-DHAVE_CONSOLE_LOG -DENABLE_LOGS_PREVIEW)
21+
22+
if(MAIN_PROJECT)
23+
find_package(opentelemetry-cpp REQUIRED)
24+
endif()
25+
26+
add_subdirectory(third_party/LinuxTracepoints)
27+
28+
include_directories(include)
29+
30+
add_library(opentelemetry_exporter_user_events_logs
31+
src/logs_exporter.cc src/recordable.cc src/utils.cc)
32+
33+
if(MAIN_PROJECT)
34+
target_include_directories(opentelemetry_exporter_user_events_logs
35+
PRIVATE ${OPENTELEMETRY_CPP_INCLUDE_DIRS})
36+
target_link_libraries(opentelemetry_exporter_user_events_logs
37+
PUBLIC ${OPENTELEMETRY_CPP_LIBRARIES})
38+
else()
39+
target_link_libraries(
40+
opentelemetry_exporter_user_events_logs
41+
PUBLIC opentelemetry_logs opentelemetry_resources opentelemetry_common)
42+
endif()
43+
44+
target_link_libraries(opentelemetry_exporter_user_events_logs
45+
PUBLIC eventheader-tracepoint tracepoint)
46+
47+
set_target_properties(opentelemetry_exporter_user_events_logs
48+
PROPERTIES EXPORT_NAME logs)
49+
50+
if(BUILD_EXAMPLE)
51+
52+
add_executable(user_events_logs example/main.cc example/foo_library.cc)
53+
target_link_libraries(
54+
user_events_logs ${CMAKE_THREAD_LIBS_INIT} opentelemetry_logs
55+
opentelemetry_exporter_user_events_logs)
56+
57+
endif(BUILD_EXAMPLE)
58+
59+
if(BUILD_TESTING)
60+
if(EXISTS ${CMAKE_BINARY_DIR}/lib/libgtest.a)
61+
# Prefer GTest from build tree. GTest is not always working with
62+
# CMAKE_PREFIX_PATH
63+
set(GTEST_INCLUDE_DIRS
64+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/googletest/googletest/include
65+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/googletest/googlemock/include)
66+
set(GTEST_BOTH_LIBRARIES
67+
${CMAKE_BINARY_DIR}/lib/libgtest.a
68+
${CMAKE_BINARY_DIR}/lib/libgtest_main.a
69+
${CMAKE_BINARY_DIR}/lib/libgmock.a)
70+
else()
71+
find_package(GTest REQUIRED)
72+
endif()
73+
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
74+
include_directories(SYSTEM test/decoder)
75+
enable_testing()
76+
include(GoogleTest)
77+
# build tests for user_events logs
78+
add_executable(userevents_logs_exporter_test test/logs_exporter_test.cc)
79+
target_link_libraries(
80+
userevents_logs_exporter_test ${GTEST_BOTH_LIBRARIES}
81+
${CMAKE_THREAD_LIBS_INIT} opentelemetry_exporter_user_events_logs)
82+
83+
gtest_add_tests(
84+
TARGET userevents_logs_exporter_test
85+
TEST_PREFIX exporter.
86+
TEST_LIST userevents_logs_exporter_test)
87+
endif()
88+
89+
find_package(benchmark CONFIG REQUIRED)
90+
add_executable(user_events_logger_benchmark benchmark/logger_benchmark.cc)
91+
target_link_libraries(
92+
user_events_logger_benchmark benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT}
93+
${CMAKE_THREAD_LIBS_INIT} opentelemetry_logs
94+
opentelemetry_exporter_user_events_logs)
95+
96+
install(
97+
TARGETS opentelemetry_exporter_user_events_logs
98+
EXPORT "${PROJECT_NAME}-target"
99+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
100+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
101+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
102+
103+
install(
104+
DIRECTORY include/opentelemetry/exporters/userevents
105+
DESTINATION include/opentelemetry/exporters
106+
FILES_MATCHING
107+
PATTERN "*.h")
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#ifdef ENABLE_LOGS_PREVIEW
5+
6+
# include "opentelemetry/common/timestamp.h"
7+
# include "opentelemetry/logs/logger.h"
8+
# include "opentelemetry/logs/provider.h"
9+
# include "opentelemetry/nostd/shared_ptr.h"
10+
11+
# include "opentelemetry/exporters/user_events/logs/exporter.h"
12+
# include "opentelemetry/logs/provider.h"
13+
# include "opentelemetry/sdk/logs/logger_provider_factory.h"
14+
# include "opentelemetry/sdk/logs/simple_log_record_processor_factory.h"
15+
16+
# include <chrono>
17+
# include <condition_variable>
18+
# include <functional>
19+
# include <mutex>
20+
# include <thread>
21+
# include <vector>
22+
23+
# include <benchmark/benchmark.h>
24+
25+
using opentelemetry::logs::EventId;
26+
using opentelemetry::logs::Logger;
27+
using opentelemetry::logs::LoggerProvider;
28+
using opentelemetry::logs::Provider;
29+
using opentelemetry::logs::Severity;
30+
using opentelemetry::nostd::shared_ptr;
31+
using opentelemetry::nostd::span;
32+
using opentelemetry::nostd::string_view;
33+
34+
namespace common = opentelemetry::common;
35+
namespace nostd = opentelemetry::nostd;
36+
namespace trace = opentelemetry::trace;
37+
namespace log_api = opentelemetry::logs;
38+
39+
namespace logs_api = opentelemetry::logs;
40+
namespace logs_sdk = opentelemetry::sdk::logs;
41+
namespace user_events_logs = opentelemetry::exporter::user_events::logs;
42+
43+
namespace
44+
{
45+
46+
constexpr int64_t kMaxIterations = 1000000;
47+
48+
class Barrier
49+
{
50+
public:
51+
explicit Barrier(std::size_t iCount) : mThreshold(iCount), mCount(iCount), mGeneration(0) {}
52+
53+
void Wait()
54+
{
55+
std::unique_lock<std::mutex> lLock{mMutex};
56+
auto lGen = mGeneration;
57+
if (!--mCount)
58+
{
59+
mGeneration++;
60+
mCount = mThreshold;
61+
mCond.notify_all();
62+
}
63+
else
64+
{
65+
mCond.wait(lLock, [this, lGen] { return lGen != mGeneration; });
66+
}
67+
}
68+
69+
private:
70+
std::mutex mMutex;
71+
std::condition_variable mCond;
72+
std::size_t mThreshold;
73+
std::size_t mCount;
74+
std::size_t mGeneration;
75+
};
76+
77+
static void ThreadRoutine(Barrier &barrier,
78+
benchmark::State &state,
79+
int thread_id,
80+
std::function<void()> func)
81+
{
82+
barrier.Wait();
83+
84+
if (thread_id == 0)
85+
{
86+
state.ResumeTiming();
87+
}
88+
89+
barrier.Wait();
90+
91+
func();
92+
93+
if (thread_id == 0)
94+
{
95+
state.PauseTiming();
96+
}
97+
98+
barrier.Wait();
99+
}
100+
101+
void MultiThreadRunner(benchmark::State &state, std::function<void()> func)
102+
{
103+
int num_threads = std::thread::hardware_concurrency();
104+
105+
Barrier barrier(num_threads);
106+
107+
std::vector<std::thread> threads;
108+
109+
for (int i = 0; i < num_threads; i++)
110+
{
111+
threads.emplace_back(ThreadRoutine, std::ref(barrier), std::ref(state), i, func);
112+
}
113+
114+
for (auto &thread : threads)
115+
{
116+
thread.join();
117+
}
118+
}
119+
120+
void InitLogger()
121+
{
122+
// Create user_events log exporter instance
123+
auto exporter_options = user_events_logs::ExporterOptions();
124+
auto exporter =
125+
std::unique_ptr<user_events_logs::Exporter>(new user_events_logs::Exporter(exporter_options));
126+
auto processor = logs_sdk::SimpleLogRecordProcessorFactory::Create(std::move(exporter));
127+
std::shared_ptr<logs_api::LoggerProvider> provider(
128+
logs_sdk::LoggerProviderFactory::Create(std::move(processor)));
129+
130+
// Set the global logger provider
131+
logs_api::Provider::SetLoggerProvider(provider);
132+
}
133+
134+
static void BM_StructuredLogWithEventIdStructAndTwoAttributes(benchmark::State &state)
135+
{
136+
InitLogger();
137+
138+
auto lp = Provider::GetLoggerProvider();
139+
auto logger = lp->GetLogger("StructuredLogWithEventId");
140+
141+
const EventId function_name_event_id{0x12345678, "Company.Component.SubComponent.FunctionName"};
142+
143+
for (auto _ : state)
144+
{
145+
state.PauseTiming();
146+
147+
MultiThreadRunner(state, [&logger, &function_name_event_id]() {
148+
for (int64_t i = 0; i < kMaxIterations; i++)
149+
{
150+
logger->Trace(
151+
function_name_event_id,
152+
"Simulate function enter trace message from {process_id}:{thread_id}",
153+
opentelemetry::common::MakeAttributes({{"process_id", 12347}, {"thread_id", 12348}}));
154+
}
155+
});
156+
157+
state.ResumeTiming();
158+
}
159+
}
160+
BENCHMARK(BM_StructuredLogWithEventIdStructAndTwoAttributes);
161+
162+
} // namespace
163+
164+
int main(int argc, char **argv)
165+
{
166+
benchmark::Initialize(&argc, argv);
167+
benchmark::RunSpecifiedBenchmarks();
168+
}
169+
170+
#endif

0 commit comments

Comments
 (0)