Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 22 additions & 23 deletions api/test/common/api/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,35 +219,34 @@ describe('API', function () {
});

describe('Global diag', function () {
it('initialization', function () {
const inst = DiagAPI.instance();
assert.deepStrictEqual(diag, inst);
});

diagLoggerFunctions.forEach(fName => {
it(`no argument logger ${fName} message doesn't throw`, function () {
// @ts-expect-error an undefined logger is not allowed
diag.setLogger();
diag[fName](`${fName} message`);
it('initialization', function () {
const inst = DiagAPI.instance();
assert.deepStrictEqual(diag, inst);
});

it(`null logger ${fName} message doesn't throw`, function () {
diag.setLogger(null as any);
diag[fName](`${fName} message`);
});
diagLoggerFunctions.forEach(fName => {
it(`no argument logger ${fName} message doesn't throw`, function () {
// @ts-expect-error an undefined logger is not allowed
diag.setLogger();
diag[fName](`${fName} message`);
});

it(`undefined logger ${fName} message doesn't throw`, function () {
diag.setLogger(undefined as any);
diag[fName](`${fName} message`);
});
it(`null logger ${fName} message doesn't throw`, function () {
diag.setLogger(null as any);
diag[fName](`${fName} message`);
});

it(`empty logger ${fName} message doesn't throw`, function () {
diag.setLogger({} as any);
diag[fName](`${fName} message`);
it(`undefined logger ${fName} message doesn't throw`, function () {
diag.setLogger(undefined as any);
diag[fName](`${fName} message`);
});

it(`empty logger ${fName} message doesn't throw`, function () {
diag.setLogger({} as any);
diag[fName](`${fName} message`);
});
});
});
});


describe('Global metrics', function () {
Comment thread
pichlermarc marked this conversation as resolved.
it('should expose a meter provider via getMeterProvider', function () {
Expand Down
1 change: 1 addition & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
* feat(sampler-composite): add ComposableAnnotatingSampler and ComposableRuleBasedSampler [#6305](https://github.com/open-telemetry/opentelemetry-js/pull/6305) @trentm
* feat(configuration): parse config for rc 3 [#6304](https://github.com/open-telemetry/opentelemetry-js/pull/6304) @maryliag
* feat(instrumentation): use the `internals: true` option with import-in-the-middle hook, allowing instrumentations to hook internal files in ES modules [#6344](https://github.com/open-telemetry/opentelemetry-js/pull/6344) @trentm
* feat(api-logs,sdk-logs): add log exception support and mapping [#6379](https://github.com/open-telemetry/opentelemetry-js/issues/6379) @iblancasa

### :bug: Bug Fixes

Expand Down
9 changes: 8 additions & 1 deletion experimental/packages/api-logs/src/types/LogRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { Context, TimeInput } from '@opentelemetry/api';
import { Context, Exception, TimeInput } from '@opentelemetry/api';
import { AnyValue, AnyValueMap } from './AnyValue';

export type LogBody = AnyValue;
Expand Down Expand Up @@ -84,6 +84,13 @@ export interface LogRecord {
*/
attributes?: LogAttributes;

/**
* An exception (or error) associated with the log record.
*
* @experimental
*/
exception?: Exception;
Comment thread
pichlermarc marked this conversation as resolved.
Outdated

/**
* The Context associated with the LogRecord.
*/
Expand Down
3 changes: 2 additions & 1 deletion experimental/packages/sdk-logs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"dependencies": {
"@opentelemetry/api-logs": "0.211.0",
"@opentelemetry/core": "2.5.0",
"@opentelemetry/resources": "2.5.0"
"@opentelemetry/resources": "2.5.0",
"@opentelemetry/semantic-conventions": "^1.39.0"
Comment thread
iblancasa marked this conversation as resolved.
Outdated
}
}
39 changes: 39 additions & 0 deletions experimental/packages/sdk-logs/src/LogRecordImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ import type {
SeverityNumber,
} from '@opentelemetry/api-logs';
import * as api from '@opentelemetry/api';
import type { Exception } from '@opentelemetry/api';
import { timeInputToHrTime, InstrumentationScope } from '@opentelemetry/core';
import type { Resource } from '@opentelemetry/resources';
import {
ATTR_EXCEPTION_MESSAGE,
ATTR_EXCEPTION_STACKTRACE,
ATTR_EXCEPTION_TYPE,
} from '@opentelemetry/semantic-conventions';
import type { ReadableLogRecord } from './export/ReadableLogRecord';
import type { LogRecordLimits } from './types';
import { isLogAttributeValue } from './utils/validation';
Expand Down Expand Up @@ -102,6 +108,7 @@ export class LogRecordImpl implements ReadableLogRecord {
severityText,
body,
attributes = {},
exception,
context,
} = logRecord;

Expand All @@ -123,6 +130,9 @@ export class LogRecordImpl implements ReadableLogRecord {
this._logRecordLimits = _sharedState.logRecordLimits;
this._eventName = eventName;
this.setAttributes(attributes);
if (exception != null) {
this._setException(exception);
}
}

public setAttribute(key: string, value?: AnyValue) {
Expand Down Expand Up @@ -231,6 +241,35 @@ export class LogRecordImpl implements ReadableLogRecord {
return value;
}

private _setException(exception: Exception): void {
const attributes: LogAttributes = {};
if (typeof exception === 'string') {
attributes[ATTR_EXCEPTION_MESSAGE] = exception;
} else if (exception) {
if (exception.code) {
attributes[ATTR_EXCEPTION_TYPE] = exception.code.toString();
} else if (exception.name) {
attributes[ATTR_EXCEPTION_TYPE] = exception.name;
}
if (exception.message) {
attributes[ATTR_EXCEPTION_MESSAGE] = exception.message;
}
if (exception.stack) {
attributes[ATTR_EXCEPTION_STACKTRACE] = exception.stack;
}
}

if (attributes[ATTR_EXCEPTION_TYPE] || attributes[ATTR_EXCEPTION_MESSAGE]) {
for (const [key, value] of Object.entries(attributes)) {
if (!Object.prototype.hasOwnProperty.call(this.attributes, key)) {
this.setAttribute(key, value);
}
}
Comment thread
iblancasa marked this conversation as resolved.
Outdated
} else {
api.diag.warn(`Failed to record an exception ${exception}`);
}
}

private _truncateToLimitUtil(value: string, limit: number): string {
if (value.length <= limit) {
return value;
Expand Down
68 changes: 68 additions & 0 deletions experimental/packages/sdk-logs/test/common/LogRecord.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Attributes,
AttributeValue,
diag,
Exception,
ROOT_CONTEXT,
trace,
TraceFlags,
Expand All @@ -29,6 +30,11 @@ import { AnyValue } from '@opentelemetry/api-logs';
import type { HrTime } from '@opentelemetry/api';
import { hrTimeToMilliseconds, timeInputToHrTime } from '@opentelemetry/core';
import { defaultResource } from '@opentelemetry/resources';
import {
ATTR_EXCEPTION_MESSAGE,
ATTR_EXCEPTION_STACKTRACE,
ATTR_EXCEPTION_TYPE,
} from '@opentelemetry/semantic-conventions';

import {
LogRecordLimits,
Expand Down Expand Up @@ -156,6 +162,68 @@ describe('LogRecord', () => {
attr2: 123,
});
});

it('should set exception attributes from exception', () => {
const error = new Error('boom');
const logRecordData: logsAPI.LogRecord = {
exception: error,
};
const { logRecord } = setup(undefined, logRecordData);

assert.strictEqual(
logRecord.attributes[ATTR_EXCEPTION_MESSAGE],
error.message
);
assert.strictEqual(logRecord.attributes[ATTR_EXCEPTION_TYPE], error.name);
if (error.stack) {
assert.strictEqual(
logRecord.attributes[ATTR_EXCEPTION_STACKTRACE],
error.stack
);
}
});

it('should not overwrite user-provided exception attributes', () => {
const error = new Error('boom');
const logRecordData: logsAPI.LogRecord = {
exception: error,
attributes: {
[ATTR_EXCEPTION_MESSAGE]: 'user message',
[ATTR_EXCEPTION_TYPE]: 'CustomError',
},
};
const { logRecord } = setup(undefined, logRecordData);

assert.strictEqual(
logRecord.attributes[ATTR_EXCEPTION_MESSAGE],
'user message'
);
assert.strictEqual(
logRecord.attributes[ATTR_EXCEPTION_TYPE],
'CustomError'
);
});

it('should set exception.message for string exceptions', () => {
const logRecordData: logsAPI.LogRecord = {
exception: 'boom',
};
const { logRecord } = setup(undefined, logRecordData);

assert.strictEqual(logRecord.attributes[ATTR_EXCEPTION_MESSAGE], 'boom');
});

it('should warn when exception has no useful fields', () => {
const warnSpy = sinon.stub(diag, 'warn');
const logRecordData: logsAPI.LogRecord = {
exception: {} as Exception,
};

setup(undefined, logRecordData);

assert.ok(warnSpy.calledOnce);
warnSpy.restore();
});
});

describe('setAttribute', () => {
Expand Down
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.