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,235 @@ 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+
92+ static nsuv::ns_mutex& ResourceMutex () {
93+ static int er = 0 ;
94+ static nsuv::ns_mutex mutex (&er, false );
95+ ASSERT_EQ (0 , er);
96+ return mutex;
97+ }
98+
99+ static std::string ToIso8601 (uint64_t timestamp_ms) {
100+ if (timestamp_ms == 0 ) return " " ;
101+
102+ const time_t seconds = static_cast <time_t >(timestamp_ms / 1000 );
103+ std::tm tm{};
104+ #ifdef _WIN32
105+ gmtime_s (&tm, &seconds);
106+ #else
107+ gmtime_r (&seconds, &tm);
108+ #endif
109+
110+ std::ostringstream stream;
111+ stream << std::put_time (&tm, " %Y-%m-%dT%H:%M:%S" );
112+
113+ const uint64_t millis = timestamp_ms % 1000 ;
114+ stream << ' .' << std::setw (3 ) << std::setfill (' 0' ) << millis;
115+
116+ stream << ' Z' ;
117+ return stream.str ();
118+ }
119+
120+ static std::string NormalizeOsType (const std::string& platform) {
121+ if (platform == " win32" ) return " windows" ;
122+ if (platform == " sunos" ) return " solaris" ;
123+ return platform;
124+ }
125+
126+ static std::string NormalizeHostArch (const std::string& arch) {
127+ if (arch == " x64" ) return " amd64" ;
128+ if (arch == " ia32" ) return " x86" ;
129+ if (arch == " arm" ) return " arm32" ;
130+ return arch;
131+ }
132+
133+ static ResourceAttributes GetMetadataResourceAttributes (const json& info) {
134+ ResourceAttributes attrs;
135+
136+ if (info.is_discarded () || !info.is_object ()) return attrs;
137+
138+ auto it = info.find (" app" );
139+ if (it != info.end () && it->is_string ()) {
140+ attrs.SetAttribute (kServiceName , it->get <std::string>());
141+ }
142+
143+ attrs.SetAttribute (kServiceInstanceId , nsolid::GetAgentId ());
144+
145+ it = info.find (" appVersion" );
146+ if (it != info.end () && it->is_string ()) {
147+ attrs.SetAttribute (kServiceVersion , it->get <std::string>());
148+ }
149+
150+ it = info.find (" hostname" );
151+ if (it != info.end () && it->is_string ()) {
152+ attrs.SetAttribute (kHostName , it->get <std::string>());
153+ }
154+
155+ it = info.find (" pid" );
156+ if (it != info.end () && it->is_number_unsigned ()) {
157+ attrs.SetAttribute (kProcessPid , static_cast <int64_t >(it->get <uint32_t >()));
158+ }
159+
160+ it = info.find (" arch" );
161+ if (it != info.end () && it->is_string ()) {
162+ attrs.SetAttribute (kHostArch , NormalizeHostArch (it->get <std::string>()));
163+ }
164+
165+ it = info.find (" platform" );
166+ if (it != info.end () && it->is_string ()) {
167+ attrs.SetAttribute (kOsType , NormalizeOsType (it->get <std::string>()));
168+ }
169+
170+ it = info.find (" execPath" );
171+ if (it != info.end () && it->is_string ()) {
172+ attrs.SetAttribute (kProcessExecutablePath , it->get <std::string>());
173+ }
174+
175+ it = info.find (" main" );
176+ if (it != info.end () && it->is_string ()) {
177+ attrs.SetAttribute (" main" , it->get <std::string>());
178+ }
179+
180+ it = info.find (" nodeEnv" );
181+ if (it != info.end () && it->is_string ()) {
182+ attrs.SetAttribute (kDeploymentEnvironmentName , it->get <std::string>());
183+ }
184+
185+ it = info.find (" versions" );
186+ if (it != info.end () && it->is_object ()) {
187+ auto version_it = it->find (" node" );
188+ if (version_it != it->end () && version_it->is_string ()) {
189+ attrs.SetAttribute (kProcessRuntimeVersion ,
190+ version_it->get <std::string>());
191+ }
192+
193+ version_it = it->find (" nsolid" );
194+ if (version_it != it->end () && version_it->is_string ()) {
195+ std::string nsolid_version = version_it->get <std::string>();
196+ attrs.SetAttribute (kProcessRuntimeDescription ,
197+ " N|Solid " + nsolid_version);
198+ }
199+ }
200+
201+ attrs.SetAttribute (kProcessRuntimeName , " nodejs" );
202+
203+ it = info.find (" cpuCores" );
204+ if (it != info.end () && it->is_number_unsigned ()) {
205+ attrs.SetAttribute (" cpuCores" , it->get <uint32_t >());
206+ }
207+
208+ it = info.find (" cpuModel" );
209+ if (it != info.end () && it->is_string ()) {
210+ attrs.SetAttribute (kHostCpuModelName , it->get <std::string>());
211+ }
212+
213+ it = info.find (" processStart" );
214+ if (it != info.end () && it->is_number_unsigned ()) {
215+ std::string iso_time = ToIso8601 (it->get <uint64_t >());
216+ if (!iso_time.empty ()) {
217+ attrs.SetAttribute (kProcessCreationTime , std::move (iso_time));
218+ }
219+ }
220+
221+ it = info.find (" tags" );
222+ if (it != info.end () && it->is_array ()) {
223+ std::string tags;
224+ for (const auto & tag : *it) {
225+ if (!tag.is_string ()) continue ;
226+ if (!tags.empty ()) tags += ' ,' ;
227+ tags += tag.get <std::string>();
228+ }
229+ attrs.SetAttribute (" tagsString" , std::move (tags));
230+ }
231+
232+ return attrs;
233+ }
234+
235+ static std::shared_ptr<Resource> MergeResourceAttributes (
236+ const std::shared_ptr<Resource>& base,
237+ ResourceAttributes attrs) {
238+ auto resource_attributes = base->GetAttributes ();
239+ if (resource_attributes.find (kServiceName ) != resource_attributes.end () &&
240+ attrs.find (kServiceName ) == attrs.end ()) {
241+ attrs.SetAttribute (
242+ kServiceName ,
243+ opentelemetry::nostd::get<std::string>(
244+ resource_attributes[kServiceName ]));
245+ }
246+ auto overlay = std::make_shared<Resource>(Resource::Create (attrs));
247+ return std::make_shared<Resource>(base->Merge (*overlay));
248+ }
249+
250+ InstrumentationScope* GetScope () {
251+ static std::unique_ptr<InstrumentationScope> scope =
252+ InstrumentationScope::Create (" nsolid" , NODE_VERSION " +ns" NSOLID_VERSION );
253+ return scope.get ();
254+ }
255+
256+ static void EnsureResourceInitializedLocked () {
257+ if (resource_g != nullptr ) return ;
258+
259+ json config = json::parse (nsolid::GetConfig (), nullptr , false );
260+ // assert because the runtime should never send me an invalid JSON config
261+ ASSERT (!config.is_discarded ());
262+ auto it = config.find (" app" );
263+ ASSERT (it != config.end ());
264+ ResourceAttributes attrs ({
265+ {kServiceName , it->get <std::string>()},
266+ {kServiceInstanceId , nsolid::GetAgentId ()}
267+ });
268+
269+ it = config.find (" appVersion" );
270+ if (it != config.end ()) {
271+ attrs.SetAttribute (kServiceVersion , it->get <std::string>());
272+ }
273+
274+ resource_g = std::make_shared<Resource>(Resource::Create (attrs));
275+ }
276+
277+ std::shared_ptr<Resource> GetResource () {
278+ nsuv::ns_mutex::scoped_lock lock (ResourceMutex ());
279+ EnsureResourceInitializedLocked ();
280+ return resource_g;
281+ }
282+
283+ static void EnsureMetricsResourceInitializedLocked () {
284+ if (metrics_resource_g != nullptr ) return ;
285+
286+ EnsureResourceInitializedLocked ();
287+
288+ json info = json::parse (nsolid::GetProcessInfo (), nullptr , false );
289+ ResourceAttributes attrs = GetMetadataResourceAttributes (info);
290+ metrics_resource_g = MergeResourceAttributes (resource_g, std::move (attrs));
291+ }
292+
293+ std::shared_ptr<Resource> GetMetricsResource () {
294+ nsuv::ns_mutex::scoped_lock lock (ResourceMutex ());
295+ EnsureMetricsResourceInitializedLocked ();
296+ return metrics_resource_g;
297+ }
298+
299+ void InvalidateMetricsResource () {
300+ nsuv::ns_mutex::scoped_lock lock (ResourceMutex ());
301+ metrics_resource_g.reset ();
302+ }
303+
304+ std::shared_ptr<Resource> UpdateResource (ResourceAttributes&& attrs) {
305+ nsuv::ns_mutex::scoped_lock lock (ResourceMutex ());
306+ EnsureResourceInitializedLocked ();
307+
308+ ResourceAttributes metrics_attrs (attrs);
309+ resource_g = MergeResourceAttributes (resource_g, std::move (attrs));
310+
311+ if (metrics_resource_g != nullptr ) {
312+ metrics_resource_g = MergeResourceAttributes (metrics_resource_g,
313+ std::move (metrics_attrs));
314+ }
315+
316+ return resource_g;
317+ }
73318
74319// NOLINTNEXTLINE(runtime/references)
75320static void add_counter (std::vector<MetricData>& metrics,
@@ -136,53 +381,6 @@ static void add_summary(std::vector<MetricData>& metrics,
136381 metrics.push_back (metric_data);
137382}
138383
139- InstrumentationScope* GetScope () {
140- static std::unique_ptr<InstrumentationScope> scope =
141- InstrumentationScope::Create (" nsolid" , NODE_VERSION " +ns" NSOLID_VERSION );
142- return scope.get ();
143- }
144-
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 ;
165- }
166-
167- return resource_g.get ();
168- }
169-
170- Resource* UpdateResource (ResourceAttributes&& attrs) {
171- // First, get current kServiceName to avoid overwriting it with the default
172- // value "unknown_service". (See Resource::Create() method in the SDK).
173- auto resource = GetResource ();
174- auto attributes = resource->GetAttributes ();
175- if (attributes.find (kServiceName ) != attributes.end () &&
176- attrs.find (kServiceName ) == attrs.end ()) {
177- attrs.SetAttribute (kServiceName ,
178- opentelemetry::nostd::get<std::string>(attributes[kServiceName ]));
179- }
180-
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 ();
184- }
185-
186384// NOLINTNEXTLINE(runtime/references)
187385void fill_proc_metrics (std::vector<MetricData>& metrics,
188386 const ProcessMetrics::MetricsStor& stor,
@@ -250,7 +448,7 @@ NSOLID_PROCESS_METRICS_DOUBLE(V)
250448 if (prev_stor.user != stor.user || prev_stor.title != stor.title ) {
251449 ResourceAttributes attrs = {
252450 { kProcessOwner , stor.user },
253- { " process.title " , stor.title },
451+ { kProcessTitle , stor.title },
254452 };
255453
256454 USE (UpdateResource (std::move (attrs)));
@@ -370,7 +568,8 @@ void fill_log_recordable(LogsRecordable* recordable,
370568 nanoseconds (static_cast <uint64_t >(info.timestamp ))));
371569 recordable->SetTimestamp (ts);
372570 recordable->SetObservedTimestamp (ts);
373- recordable->SetResource (*GetResource ());
571+ auto resource = GetResource ();
572+ recordable->SetResource (*resource);
374573 recordable->SetInstrumentationScope (*GetScope ());
375574}
376575
@@ -508,7 +707,8 @@ void fill_recordable(Recordable* recordable, const Tracer::SpanStor& s) {
508707 recordable->SetAttribute (" thread.id" , s.thread_id );
509708 recordable->SetAttribute (" nsolid.span_type" , s.type );
510709
511- recordable->SetResource (*GetResource ());
710+ auto resource = GetResource ();
711+ recordable->SetResource (*resource);
512712}
513713
514714} // namespace otlp
0 commit comments