11#include " otlp_common.h"
22// NOLINTNEXTLINE(build/c++11)
33#include < chrono>
4+ #include < ctime>
5+ #include < iomanip>
6+ #include < sstream>
47#include < unordered_map>
58#include " asserts-cpp/asserts.h"
69#include " env-inl.h"
710#include " nlohmann/json.hpp"
11+ #include " nsuv-inl.h"
12+ #include " opentelemetry/semconv/incubating/deployment_attributes.h"
13+ #include " opentelemetry/semconv/incubating/host_attributes.h"
14+ #include " opentelemetry/semconv/incubating/os_attributes.h"
815#include " opentelemetry/semconv/incubating/process_attributes.h"
916#include " opentelemetry/semconv/incubating/service_attributes.h"
1017#include " opentelemetry/semconv/incubating/thread_attributes.h"
@@ -44,8 +51,20 @@ using opentelemetry::trace::SpanId;
4451using opentelemetry::trace::SpanKind;
4552using opentelemetry::trace::TraceFlags;
4653using opentelemetry::trace::TraceId;
54+ using opentelemetry::semconv::deployment::kDeploymentEnvironmentName ;
55+ using opentelemetry::semconv::host::kHostArch ;
56+ using opentelemetry::semconv::host::kHostCpuModelName ;
57+ using opentelemetry::semconv::host::kHostName ;
58+ using opentelemetry::semconv::os::kOsType ;
4759using opentelemetry::trace::propagation::detail::HexToBinary;
60+ using opentelemetry::semconv::process::kProcessCreationTime ;
61+ using opentelemetry::semconv::process::kProcessExecutablePath ;
4862using opentelemetry::semconv::process::kProcessOwner ;
63+ using opentelemetry::semconv::process::kProcessPid ;
64+ using opentelemetry::semconv::process::kProcessRuntimeDescription ;
65+ using opentelemetry::semconv::process::kProcessRuntimeName ;
66+ using opentelemetry::semconv::process::kProcessRuntimeVersion ;
67+ using opentelemetry::semconv::process::kProcessTitle ;
4968using opentelemetry::semconv::service::kServiceName ;
5069using opentelemetry::semconv::service::kServiceInstanceId ;
5170using opentelemetry::semconv::service::kServiceVersion ;
@@ -67,9 +86,145 @@ static std::vector<std::string> discarded_metrics = {
6786 " thread_id" , " timestamp"
6887};
6988
70- static std::unique_ptr<Resource> resource_g =
71- std::make_unique<Resource>(Resource::GetEmpty());
72- static bool isResourceInitialized_g = false ;
89+ static std::shared_ptr<Resource> resource_g;
90+ static std::shared_ptr<Resource> metrics_resource_g;
91+ static nsuv::ns_mutex resource_mutex_g;
92+
93+ static std::string ToIso8601 (uint64_t timestamp_ms) {
94+ if (timestamp_ms == 0 ) return " " ;
95+
96+ const time_t seconds = static_cast <time_t >(timestamp_ms / 1000 );
97+ std::tm tm{};
98+ #ifdef _WIN32
99+ gmtime_s (&tm, &seconds);
100+ #else
101+ gmtime_r (&seconds, &tm);
102+ #endif
103+
104+ std::ostringstream stream;
105+ stream << std::put_time (&tm, " %Y-%m-%dT%H:%M:%S" );
106+
107+ const uint64_t millis = timestamp_ms % 1000 ;
108+ stream << ' .' << std::setw (3 ) << std::setfill (' 0' ) << millis;
109+
110+ stream << ' Z' ;
111+ return stream.str ();
112+ }
113+
114+ static std::string NormalizeOsType (const std::string& platform) {
115+ if (platform == " win32" ) return " windows" ;
116+ if (platform == " sunos" ) return " solaris" ;
117+ return platform;
118+ }
119+
120+ static std::string NormalizeHostArch (const std::string& arch) {
121+ if (arch == " x64" ) return " amd64" ;
122+ if (arch == " ia32" ) return " x86" ;
123+ if (arch == " arm" ) return " arm32" ;
124+ return arch;
125+ }
126+
127+ static ResourceAttributes GetMetadataResourceAttributes (const json& info) {
128+ ResourceAttributes attrs;
129+
130+ if (info.is_discarded () || !info.is_object ()) return attrs;
131+
132+ auto it = info.find (" app" );
133+ if (it != info.end () && it->is_string ()) {
134+ attrs.SetAttribute (kServiceName , it->get <std::string>());
135+ }
136+
137+ attrs.SetAttribute (kServiceInstanceId , nsolid::GetAgentId ());
138+
139+ it = info.find (" appVersion" );
140+ if (it != info.end () && it->is_string ()) {
141+ attrs.SetAttribute (kServiceVersion , it->get <std::string>());
142+ }
143+
144+ it = info.find (" hostname" );
145+ if (it != info.end () && it->is_string ()) {
146+ attrs.SetAttribute (kHostName , it->get <std::string>());
147+ }
148+
149+ it = info.find (" pid" );
150+ if (it != info.end () && it->is_number_unsigned ()) {
151+ attrs.SetAttribute (kProcessPid , static_cast <int64_t >(it->get <uint32_t >()));
152+ }
153+
154+ it = info.find (" arch" );
155+ if (it != info.end () && it->is_string ()) {
156+ attrs.SetAttribute (kHostArch , NormalizeHostArch (it->get <std::string>()));
157+ }
158+
159+ it = info.find (" platform" );
160+ if (it != info.end () && it->is_string ()) {
161+ attrs.SetAttribute (kOsType , NormalizeOsType (it->get <std::string>()));
162+ }
163+
164+ it = info.find (" execPath" );
165+ if (it != info.end () && it->is_string ()) {
166+ attrs.SetAttribute (kProcessExecutablePath , it->get <std::string>());
167+ }
168+
169+ it = info.find (" main" );
170+ if (it != info.end () && it->is_string ()) {
171+ attrs.SetAttribute (" main" , it->get <std::string>());
172+ }
173+
174+ it = info.find (" nodeEnv" );
175+ if (it != info.end () && it->is_string ()) {
176+ attrs.SetAttribute (kDeploymentEnvironmentName , it->get <std::string>());
177+ }
178+
179+ it = info.find (" versions" );
180+ if (it != info.end () && it->is_object ()) {
181+ auto version_it = it->find (" node" );
182+ if (version_it != it->end () && version_it->is_string ()) {
183+ attrs.SetAttribute (kProcessRuntimeVersion ,
184+ version_it->get <std::string>());
185+ }
186+
187+ version_it = it->find (" nsolid" );
188+ if (version_it != it->end () && version_it->is_string ()) {
189+ std::string nsolid_version = version_it->get <std::string>();
190+ attrs.SetAttribute (kProcessRuntimeDescription ,
191+ " N|Solid " + nsolid_version);
192+ }
193+ }
194+
195+ attrs.SetAttribute (kProcessRuntimeName , " nodejs" );
196+
197+ it = info.find (" cpuCores" );
198+ if (it != info.end () && it->is_number_unsigned ()) {
199+ attrs.SetAttribute (" cpuCores" , std::to_string (it->get <uint32_t >()));
200+ }
201+
202+ it = info.find (" cpuModel" );
203+ if (it != info.end () && it->is_string ()) {
204+ attrs.SetAttribute (kHostCpuModelName , it->get <std::string>());
205+ }
206+
207+ it = info.find (" processStart" );
208+ if (it != info.end () && it->is_number_unsigned ()) {
209+ std::string iso_time = ToIso8601 (it->get <uint64_t >());
210+ if (!iso_time.empty ()) {
211+ attrs.SetAttribute (kProcessCreationTime , std::move (iso_time));
212+ }
213+ }
214+
215+ it = info.find (" tags" );
216+ if (it != info.end () && it->is_array ()) {
217+ std::string tags;
218+ for (const auto & tag : *it) {
219+ if (!tag.is_string ()) continue ;
220+ if (!tags.empty ()) tags += ' ,' ;
221+ tags += tag.get <std::string>();
222+ }
223+ attrs.SetAttribute (" tagsString" , std::move (tags));
224+ }
225+
226+ return attrs;
227+ }
73228
74229// NOLINTNEXTLINE(runtime/references)
75230static void add_counter (std::vector<MetricData>& metrics,
@@ -142,45 +297,86 @@ InstrumentationScope* GetScope() {
142297 return scope.get ();
143298}
144299
145- Resource* GetResource () {
146- if (!isResourceInitialized_g) {
147- json config = json::parse (nsolid::GetConfig (), nullptr , false );
148- // assert because the runtime should never send me an invalid JSON config
149- ASSERT (!config.is_discarded ());
150- auto it = config.find (" app" );
151- ASSERT (it != config.end ());
152- ResourceAttributes attrs ({
153- {kServiceName , it->get <std::string>()},
154- {kServiceInstanceId , nsolid::GetAgentId ()}
155- });
156-
157- it = config.find (" appVersion" );
158- if (it != config.end ()) {
159- attrs.SetAttribute (kServiceVersion , it->get <std::string>());
160- }
161-
162- // Directly construct a new Resource in the unique_ptr
163- resource_g = std::make_unique<Resource>(Resource::Create (attrs));
164- isResourceInitialized_g = true ;
300+ static void EnsureResourceInitializedLocked () {
301+ if (resource_g != nullptr ) return ;
302+
303+ json config = json::parse (nsolid::GetConfig (), nullptr , false );
304+ // assert because the runtime should never send me an invalid JSON config
305+ ASSERT (!config.is_discarded ());
306+ auto it = config.find (" app" );
307+ ASSERT (it != config.end ());
308+ ResourceAttributes attrs ({
309+ {kServiceName , it->get <std::string>()},
310+ {kServiceInstanceId , nsolid::GetAgentId ()}
311+ });
312+
313+ it = config.find (" appVersion" );
314+ if (it != config.end ()) {
315+ attrs.SetAttribute (kServiceVersion , it->get <std::string>());
165316 }
166317
167- return resource_g. get ( );
318+ resource_g = std::make_shared<Resource>( Resource::Create (attrs) );
168319}
169320
170- Resource* UpdateResource (ResourceAttributes&& attrs) {
321+ std::shared_ptr<Resource> GetResource () {
322+ nsuv::ns_mutex::scoped_lock lock (resource_mutex_g);
323+ EnsureResourceInitializedLocked ();
324+ return resource_g;
325+ }
326+
327+ static void EnsureMetricsResourceInitializedLocked () {
328+ if (metrics_resource_g != nullptr ) return ;
329+
330+ EnsureResourceInitializedLocked ();
331+
332+ json info = json::parse (nsolid::GetProcessInfo (), nullptr , false );
333+ ResourceAttributes attrs = GetMetadataResourceAttributes (info);
334+ auto new_res = std::make_shared<Resource>(Resource::Create (attrs));
335+ metrics_resource_g =
336+ std::make_shared<Resource>(resource_g->Merge (*new_res));
337+ }
338+
339+ std::shared_ptr<Resource> GetMetricsResource () {
340+ nsuv::ns_mutex::scoped_lock lock (resource_mutex_g);
341+ EnsureMetricsResourceInitializedLocked ();
342+ return metrics_resource_g;
343+ }
344+
345+ std::shared_ptr<Resource> UpdateResource (ResourceAttributes&& attrs) {
346+ nsuv::ns_mutex::scoped_lock lock (resource_mutex_g);
347+ EnsureResourceInitializedLocked ();
348+
349+ ResourceAttributes metrics_attrs (attrs);
350+
171351 // First, get current kServiceName to avoid overwriting it with the default
172352 // value "unknown_service". (See Resource::Create() method in the SDK).
173- auto resource = GetResource ();
174- auto attributes = resource->GetAttributes ();
353+ auto attributes = resource_g->GetAttributes ();
175354 if (attributes.find (kServiceName ) != attributes.end () &&
176355 attrs.find (kServiceName ) == attrs.end ()) {
177356 attrs.SetAttribute (kServiceName ,
178357 opentelemetry::nostd::get<std::string>(attributes[kServiceName ]));
179358 }
180359
181- auto new_res = std::make_unique<Resource>(Resource::Create (attrs));
182- resource_g = std::make_unique<Resource>(resource->Merge (*new_res));
183- return resource_g.get ();
360+ auto new_res = std::make_shared<Resource>(Resource::Create (attrs));
361+ resource_g = std::make_shared<Resource>(resource_g->Merge (*new_res));
362+
363+ if (metrics_resource_g != nullptr ) {
364+ auto metrics_attributes = metrics_resource_g->GetAttributes ();
365+ if (metrics_attributes.find (kServiceName ) != metrics_attributes.end () &&
366+ metrics_attrs.find (kServiceName ) == metrics_attrs.end ()) {
367+ metrics_attrs.SetAttribute (
368+ kServiceName ,
369+ opentelemetry::nostd::get<std::string>(
370+ metrics_attributes[kServiceName ]));
371+ }
372+
373+ auto metrics_new_res =
374+ std::make_shared<Resource>(Resource::Create (metrics_attrs));
375+ metrics_resource_g =
376+ std::make_shared<Resource>(metrics_resource_g->Merge (*metrics_new_res));
377+ }
378+
379+ return resource_g;
184380}
185381
186382// NOLINTNEXTLINE(runtime/references)
@@ -250,7 +446,7 @@ NSOLID_PROCESS_METRICS_DOUBLE(V)
250446 if (prev_stor.user != stor.user || prev_stor.title != stor.title ) {
251447 ResourceAttributes attrs = {
252448 { kProcessOwner , stor.user },
253- { " process.title " , stor.title },
449+ { kProcessTitle , stor.title },
254450 };
255451
256452 USE (UpdateResource (std::move (attrs)));
@@ -370,7 +566,8 @@ void fill_log_recordable(LogsRecordable* recordable,
370566 nanoseconds (static_cast <uint64_t >(info.timestamp ))));
371567 recordable->SetTimestamp (ts);
372568 recordable->SetObservedTimestamp (ts);
373- recordable->SetResource (*GetResource ());
569+ auto resource = GetResource ();
570+ recordable->SetResource (*resource);
374571 recordable->SetInstrumentationScope (*GetScope ());
375572}
376573
@@ -508,7 +705,8 @@ void fill_recordable(Recordable* recordable, const Tracer::SpanStor& s) {
508705 recordable->SetAttribute (" thread.id" , s.thread_id );
509706 recordable->SetAttribute (" nsolid.span_type" , s.type );
510707
511- recordable->SetResource (*GetResource ());
708+ auto resource = GetResource ();
709+ recordable->SetResource (*resource);
512710}
513711
514712} // namespace otlp
0 commit comments