|
| 1 | +import * as net from 'node:net'; |
1 | 2 | import type { Span, Tracer } from '@opentelemetry/api';
|
2 | 3 | import { context, diag, SpanKind, trace } from '@opentelemetry/api';
|
3 | 4 | import {
|
@@ -251,67 +252,77 @@ function startDBSpan<AppModelType, DbModelType extends DocumentData>(
|
251 | 252 | return span;
|
252 | 253 | }
|
253 | 254 |
|
254 |
| -function addAttributes<AppModelType, DbModelType extends DocumentData>( |
255 |
| - span: Span, |
256 |
| - reference: CollectionReference<AppModelType, DbModelType> | DocumentReference<AppModelType, DbModelType>, |
257 |
| -): void { |
258 |
| - const firestoreApp: FirebaseApp = reference.firestore.app; |
259 |
| - const firestoreOptions: FirebaseOptions = firestoreApp.options; |
260 |
| - const json: { settings?: FirestoreSettings } = reference.firestore.toJSON() || {}; |
261 |
| - const settings: FirestoreSettings = json.settings || {}; |
262 |
| - |
263 |
| - const attributes: SpanAttributes = { |
264 |
| - [ATTR_DB_COLLECTION_NAME]: reference.path, |
265 |
| - [ATTR_DB_NAMESPACE]: firestoreApp.name, |
266 |
| - [ATTR_DB_SYSTEM_NAME]: 'firebase.firestore', |
267 |
| - 'firebase.firestore.type': reference.type, |
268 |
| - 'firebase.firestore.options.projectId': firestoreOptions.projectId, |
269 |
| - 'firebase.firestore.options.appId': firestoreOptions.appId, |
270 |
| - 'firebase.firestore.options.messagingSenderId': firestoreOptions.messagingSenderId, |
271 |
| - 'firebase.firestore.options.storageBucket': firestoreOptions.storageBucket, |
272 |
| - }; |
273 |
| - |
| 255 | +/** |
| 256 | + * Sets the server address and port attributes on the span based on the Firestore settings. |
| 257 | + * It's best effort to extract the address and port from the settings, especially for IPv6. |
| 258 | + * @param span - The span to set attributes on. |
| 259 | + * @param settings - The Firestore settings containing host information. |
| 260 | + */ |
| 261 | +function setPortAndAddress(span: Span, settings: FirestoreSettings): void { |
274 | 262 | if (typeof settings.host === 'string') {
|
275 | 263 | let address: string | undefined;
|
276 | 264 | let port: string | undefined;
|
277 | 265 |
|
278 | 266 | if (settings.host.startsWith('[')) {
|
| 267 | + // IPv6 addresses can be enclosed in square brackets, e.g., [2001:db8::1]:8080 |
279 | 268 | if (settings.host.endsWith(']')) {
|
280 |
| - // Theres no port, just the address |
| 269 | + // IPv6 with square brackets without port |
281 | 270 | address = settings.host.slice(1, -1);
|
282 | 271 | } else {
|
283 |
| - // Handling IPv6 addresses with port |
| 272 | + // IPv6 with square brackets with port |
284 | 273 | const lastColonIndex = settings.host.lastIndexOf(':');
|
285 | 274 | if (lastColonIndex !== -1) {
|
286 | 275 | address = settings.host.slice(1, lastColonIndex);
|
287 | 276 | port = settings.host.slice(lastColonIndex + 1);
|
288 | 277 | }
|
289 | 278 | }
|
290 | 279 | } else {
|
291 |
| - if (settings.host.includes('::')) { |
292 |
| - // Handling IPv6 addresses with port |
293 |
| - const parts = settings.host.split(':'); |
294 |
| - address = parts.slice(0, -1).join(':'); |
295 |
| - port = parts[parts.length - 1]; |
296 |
| - } else if (settings.host.includes(':')) { |
297 |
| - // Handling IPv4 addresses with port |
298 |
| - const parts = settings.host.split(':'); |
299 |
| - address = parts[0]; |
300 |
| - port = parts[1]; |
301 |
| - } else { |
302 |
| - // Handling IPv4 addresses without port |
| 280 | + // IPv4 or IPv6 without square brackets |
| 281 | + // If it's an IPv6 address without square brackets, we assume it does not have a port. |
| 282 | + if (net.isIPv6(settings.host)) { |
303 | 283 | address = settings.host;
|
304 | 284 | }
|
| 285 | + // If it's an IPv4 address, we can extract the port if it exists. |
| 286 | + else { |
| 287 | + const lastColonIndex = settings.host.lastIndexOf(':'); |
| 288 | + if (lastColonIndex !== -1) { |
| 289 | + address = settings.host.slice(0, lastColonIndex); |
| 290 | + port = settings.host.slice(lastColonIndex + 1); |
| 291 | + } else { |
| 292 | + address = settings.host; |
| 293 | + } |
| 294 | + } |
305 | 295 | }
|
306 |
| - |
307 | 296 | if (address) {
|
308 |
| - attributes[ATTR_SERVER_ADDRESS] = address; |
| 297 | + span.setAttribute(ATTR_SERVER_ADDRESS, address); |
309 | 298 | }
|
310 | 299 |
|
311 | 300 | if (port !== undefined) {
|
312 |
| - attributes[ATTR_SERVER_PORT] = Number(port); |
| 301 | + span.setAttribute(ATTR_SERVER_PORT, Number(port)); |
313 | 302 | }
|
314 | 303 | }
|
| 304 | +} |
| 305 | + |
| 306 | +function addAttributes<AppModelType, DbModelType extends DocumentData>( |
| 307 | + span: Span, |
| 308 | + reference: CollectionReference<AppModelType, DbModelType> | DocumentReference<AppModelType, DbModelType>, |
| 309 | +): void { |
| 310 | + const firestoreApp: FirebaseApp = reference.firestore.app; |
| 311 | + const firestoreOptions: FirebaseOptions = firestoreApp.options; |
| 312 | + const json: { settings?: FirestoreSettings } = reference.firestore.toJSON() || {}; |
| 313 | + const settings: FirestoreSettings = json.settings || {}; |
| 314 | + |
| 315 | + const attributes: SpanAttributes = { |
| 316 | + [ATTR_DB_COLLECTION_NAME]: reference.path, |
| 317 | + [ATTR_DB_NAMESPACE]: firestoreApp.name, |
| 318 | + [ATTR_DB_SYSTEM_NAME]: 'firebase.firestore', |
| 319 | + 'firebase.firestore.type': reference.type, |
| 320 | + 'firebase.firestore.options.projectId': firestoreOptions.projectId, |
| 321 | + 'firebase.firestore.options.appId': firestoreOptions.appId, |
| 322 | + 'firebase.firestore.options.messagingSenderId': firestoreOptions.messagingSenderId, |
| 323 | + 'firebase.firestore.options.storageBucket': firestoreOptions.storageBucket, |
| 324 | + }; |
315 | 325 |
|
316 | 326 | span.setAttributes(attributes);
|
| 327 | + setPortAndAddress(span, settings); |
317 | 328 | }
|
0 commit comments