3 Commits
Author SHA1 Message Date
semantic-release-bot 292f44821d chore(release): 1.0.1 [skip ci]
## [1.0.1](https://git.mifi.dev/mifi/logger/compare/v1.0.0...v1.0.1) (2026-08-07)

### Bug Fixes

* **sentry:** widen SentryLike so official SDKs assign without adapters ([9f6f6e4](9f6f6e4df5))
* **sentry:** widen SentryLike so official SDKs assign without adapters ([3c468db](3c468dbf44))
2026-08-07 19:08:20 +00:00
mifi 9f6f6e4df5 fix(sentry): widen SentryLike so official SDKs assign without adapters
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful
2026-08-07 16:06:22 -03:00
mifi 3c468dbf44 fix(sentry): widen SentryLike so official SDKs assign without adapters
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/push/publish unknown status
2026-08-07 16:05:09 -03:00
7 changed files with 520 additions and 17 deletions
+12 -6
View File
@@ -1,16 +1,22 @@
# [1.0.0](https://git.mifi.dev/mifi/logger/compare/v0.10.0...v1.0.0) (2026-08-07) ## [1.0.1](https://git.mifi.dev/mifi/logger/compare/v1.0.0...v1.0.1) (2026-08-07)
### Bug Fixes
* **sentry:** widen SentryLike so official SDKs assign without adapters ([9f6f6e4](https://git.mifi.dev/mifi/logger/commit/9f6f6e4df52449a84111656a939fda5f59816ea5))
* **sentry:** widen SentryLike so official SDKs assign without adapters ([3c468db](https://git.mifi.dev/mifi/logger/commit/3c468dbf44caf119b4ff769cbc158a4c3d9f6c54))
# [1.0.0](https://git.mifi.dev/mifi/logger/compare/v0.10.0...v1.0.0) (2026-08-07)
### Features ### Features
* API options object support to control sentry handling; removal of `logger.sentry.` ([234226e](https://git.mifi.dev/mifi/logger/commit/234226e2bbb79acf3b2686890677bd7326c6a0e0)) - API options object support to control sentry handling; removal of `logger.sentry.` ([234226e](https://git.mifi.dev/mifi/logger/commit/234226e2bbb79acf3b2686890677bd7326c6a0e0))
* API options object support to control sentry handling; removal of `logger.sentry.` ([6bee3a8](https://git.mifi.dev/mifi/logger/commit/6bee3a801da054a613e84d7985050e7e4e367194)) - API options object support to control sentry handling; removal of `logger.sentry.` ([6bee3a8](https://git.mifi.dev/mifi/logger/commit/6bee3a801da054a613e84d7985050e7e4e367194))
### BREAKING CHANGES ### BREAKING CHANGES
* API options object support to control sentry handling; removal of `logger.sentry.` - API options object support to control sentry handling; removal of `logger.sentry.`
* API options object support to control sentry handling; removal of `logger.sentry.` - API options object support to control sentry handling; removal of `logger.sentry.`
# [0.10.0](https://git.mifi.dev/mifi/logger/compare/v0.9.3...v0.10.0) (2026-08-05) # [0.10.0](https://git.mifi.dev/mifi/logger/compare/v0.9.3...v0.10.0) (2026-08-05)
+4 -2
View File
@@ -95,7 +95,7 @@ Copy [`env.example`](./env.example) as a starting point for local or container c
## Sentry ## Sentry
The Sentry adapter has **no SDK dependency**. Provide the SDK your application already uses via `@mifi/logger/sentry`. The Sentry adapter has **no SDK dependency**. Pass your official `@sentry/node`, `@sentry/browser`, or `@sentry/nextjs` module directly to `createSentrySink` — no consumer adapter or cast is required.
**Development never sends to Sentry.** Staging and production attach the sink when `sentry` is configured. **Development never sends to Sentry.** Staging and production attach the sink when `sentry` is configured.
@@ -152,6 +152,8 @@ A last argument is treated as options only when every key is `sentry` or `suppre
Logs payload: string message + flat primitive attributes (including `logger.namespace`). Nested objects are not unfurled. Logs payload: string message + flat primitive attributes (including `logger.namespace`). Nested objects are not unfurled.
Issue severities use Sentrys union (`warning`, not logger `warn`). The sink maps `warn``warning` and `trace``debug` for Issues only; Logs keep logger level names (`sentry.logger.warn`).
## Custom sinks ## Custom sinks
Replace the default destinations entirely with `sinks`: Replace the default destinations entirely with `sinks`:
@@ -182,7 +184,7 @@ When `sinks` is provided, the `sentry` option is not used for default sink selec
| `parseLoggingOverride` | `@mifi/logger` | Parse `debug` / `debug:NS1,NS2` override strings | | `parseLoggingOverride` | `@mifi/logger` | Parse `debug` / `debug:NS1,NS2` override strings |
| `createSentrySink` | `@mifi/logger/sentry` | Adapter for any Sentry-like SDK | | `createSentrySink` | `@mifi/logger/sentry` | Adapter for any Sentry-like SDK |
Types: `Logger`, `LoggerOptions`, `LogLevel`, `LoggerEnvironment`, `LogEvent`, `LogSink`, `LogData`, `LogCallOptions`, and (from `/sentry`) `SentryLike`, `SentryScopeLike`, `SentrySink`, `SentrySinkOptions`. Types: `Logger`, `LoggerOptions`, `LogLevel`, `LoggerEnvironment`, `LogEvent`, `LogSink`, `LogData`, `LogCallOptions`, and (from `/sentry`) `SentryLike`, `SentryScopeLike`, `SentrySeverityLevel`, `SentrySink`, `SentrySinkOptions`.
Optional destinations live under `src/sinks/` (console, Sentry today; New Relic / Datadog-style adapters can land beside them later). Public import paths stay `@mifi/logger` and `@mifi/logger/sentry`. Optional destinations live under `src/sinks/` (console, Sentry today; New Relic / Datadog-style adapters can land beside them later). Public import paths stay `@mifi/logger` and `@mifi/logger/sentry`.
+2 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@mifi/logger", "name": "@mifi/logger",
"version": "1.0.0", "version": "1.0.1",
"description": "Intentional, namespaced logging for browser and Node.js TypeScript applications.", "description": "Intentional, namespaced logging for browser and Node.js TypeScript applications.",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -45,6 +45,7 @@
"devDependencies": { "devDependencies": {
"@semantic-release/changelog": "^7.0.0", "@semantic-release/changelog": "^7.0.0",
"@semantic-release/git": "^11.0.1", "@semantic-release/git": "^11.0.1",
"@sentry/node": "^10.69.0",
"@types/node": "^24.0.0", "@types/node": "^24.0.0",
"@vitest/coverage-v8": "^3.0.0", "@vitest/coverage-v8": "^3.0.0",
"oxlint": "^1.0.0", "oxlint": "^1.0.0",
+368
View File
@@ -13,6 +13,9 @@ importers:
"@semantic-release/git": "@semantic-release/git":
specifier: ^11.0.1 specifier: ^11.0.1
version: 11.0.1(semantic-release@25.0.8(typescript@6.0.3)) version: 11.0.1(semantic-release@25.0.8(typescript@6.0.3))
"@sentry/node":
specifier: ^10.69.0
version: 10.69.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))
"@types/node": "@types/node":
specifier: ^24.0.0 specifier: ^24.0.0
version: 24.13.3 version: 24.13.3
@@ -70,6 +73,26 @@ packages:
} }
engines: { node: ">=6.0.0" } engines: { node: ">=6.0.0" }
"@apm-js-collab/code-transformer-bundler-plugins@0.7.4":
resolution:
{
integrity: sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg==,
}
engines: { node: ">=18.0.0" }
"@apm-js-collab/code-transformer@0.18.1":
resolution:
{
integrity: sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==,
}
hasBin: true
"@apm-js-collab/tracing-hooks@0.13.0":
resolution:
{
integrity: sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==,
}
"@babel/code-frame@7.29.7": "@babel/code-frame@7.29.7":
resolution: resolution:
{ {
@@ -730,6 +753,72 @@ packages:
integrity: sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==, integrity: sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==,
} }
"@opentelemetry/api-logs@0.220.0":
resolution:
{
integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==,
}
engines: { node: ">=8.0.0" }
"@opentelemetry/api@1.9.1":
resolution:
{
integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==,
}
engines: { node: ">=8.0.0" }
"@opentelemetry/core@2.10.0":
resolution:
{
integrity: sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==,
}
engines: { node: ^18.19.0 || >=20.6.0 }
peerDependencies:
"@opentelemetry/api": ">=1.0.0 <1.10.0"
"@opentelemetry/instrumentation@0.220.0":
resolution:
{
integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==,
}
engines: { node: ^18.19.0 || >=20.6.0 }
peerDependencies:
"@opentelemetry/api": ^1.3.0
"@opentelemetry/resources@2.10.0":
resolution:
{
integrity: sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==,
}
engines: { node: ^18.19.0 || >=20.6.0 }
peerDependencies:
"@opentelemetry/api": ">=1.3.0 <1.10.0"
"@opentelemetry/sdk-trace-base@2.10.0":
resolution:
{
integrity: sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==,
}
engines: { node: ^18.19.0 || >=20.6.0 }
peerDependencies:
"@opentelemetry/api": ">=1.3.0 <1.10.0"
"@opentelemetry/sdk-trace@2.10.0":
resolution:
{
integrity: sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==,
}
engines: { node: ^18.19.0 || >=20.6.0 }
peerDependencies:
"@opentelemetry/api": ">=1.3.0 <1.10.0"
"@opentelemetry/semantic-conventions@1.43.0":
resolution:
{
integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==,
}
engines: { node: ">=14" }
"@oxlint/binding-android-arm-eabi@1.77.0": "@oxlint/binding-android-arm-eabi@1.77.0":
resolution: resolution:
{ {
@@ -1217,6 +1306,69 @@ packages:
peerDependencies: peerDependencies:
semantic-release: ">=20.1.0" semantic-release: ">=20.1.0"
"@sentry/conventions@0.16.0":
resolution:
{
integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==,
}
engines: { node: ">=14" }
"@sentry/core@10.69.0":
resolution:
{
integrity: sha512-+uuqVEeiDzYuAKjZLqsROKXvRTbl/QeH0gfGRtpYib1cud4rAFWRIkFmcR7Jb7JGFYwmReyQotiTj/hcDszTZg==,
}
engines: { node: ">=18" }
"@sentry/node-core@10.69.0":
resolution:
{
integrity: sha512-IgArHczrZJxkgxoffHscj0NxQrG6kCazgmGQnlf3j58J1ec21YaUu8Tu+7G4Lo5tCiW3teQnwlKW1ttMXSqWRw==,
}
engines: { node: ">=18" }
peerDependencies:
"@opentelemetry/api": ^1.9.0
"@opentelemetry/core": ^1.30.1 || ^2.1.0
"@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1"
"@opentelemetry/instrumentation": ">=0.57.1 <1"
"@opentelemetry/sdk-trace-base": ^1.30.1 || ^2.1.0
peerDependenciesMeta:
"@opentelemetry/api":
optional: true
"@opentelemetry/core":
optional: true
"@opentelemetry/exporter-trace-otlp-http":
optional: true
"@opentelemetry/instrumentation":
optional: true
"@opentelemetry/sdk-trace-base":
optional: true
"@sentry/node@10.69.0":
resolution:
{
integrity: sha512-xEXA1YGIiTZbrW6MWV34uS6JGQuQg2ijTI0zed+FsJb9JZKPYel/GZK8Km26vfTVb+yCXFmWZNBesKegNcVdzg==,
}
engines: { node: ">=18" }
"@sentry/opentelemetry@10.69.0":
resolution:
{
integrity: sha512-3FyWV6YcEJuvLrlaKGE1dHXCI+1YO0a62w7PkwlRg8yp6K6YXkmdwu9GjqaYD+Ju4tm7uC7mHIsGFQMm0M7pqQ==,
}
engines: { node: ">=18" }
peerDependencies:
"@opentelemetry/api": ^1.9.0
"@opentelemetry/core": ^1.30.1 || ^2.1.0
"@opentelemetry/sdk-trace-base": ^1.30.1 || ^2.1.0
"@sentry/server-utils@10.69.0":
resolution:
{
integrity: sha512-0MwHrA8+nNvMIsqf8m3cXwCBlUjr6AS7N6CZvHJtY1DkqEvQqEbD5VIrhzEyHN/KMZIgQ8XeDCQRhjnXFQGRhg==,
}
engines: { node: ">=18" }
"@simple-libs/stream-utils@1.2.0": "@simple-libs/stream-utils@1.2.0":
resolution: resolution:
{ {
@@ -1431,6 +1583,13 @@ packages:
integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==, integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==,
} }
astring@1.9.0:
resolution:
{
integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==,
}
hasBin: true
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: resolution:
{ {
@@ -1548,6 +1707,12 @@ packages:
} }
engines: { node: ">= 14.16.0" } engines: { node: ">= 14.16.0" }
cjs-module-lexer@2.2.0:
resolution:
{
integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==,
}
clean-stack@5.3.0: clean-stack@5.3.0:
resolution: resolution:
{ {
@@ -1825,6 +1990,12 @@ packages:
integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==,
} }
es-module-lexer@2.3.1:
resolution:
{
integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==,
}
esbuild@0.27.7: esbuild@0.27.7:
resolution: resolution:
{ {
@@ -1862,6 +2033,20 @@ packages:
} }
engines: { node: ">=12" } engines: { node: ">=12" }
esquery@1.7.0:
resolution:
{
integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==,
}
engines: { node: ">=0.10" }
estraverse@5.3.0:
resolution:
{
integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==,
}
engines: { node: ">=4.0" }
estree-walker@3.0.3: estree-walker@3.0.3:
resolution: resolution:
{ {
@@ -2143,6 +2328,13 @@ packages:
} }
engines: { node: ">=18.20" } engines: { node: ">=18.20" }
import-in-the-middle@3.3.3:
resolution:
{
integrity: sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==,
}
engines: { node: ">=18" }
import-meta-resolve@4.2.0: import-meta-resolve@4.2.0:
resolution: resolution:
{ {
@@ -2491,6 +2683,13 @@ packages:
integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==, integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==,
} }
meriyah@6.1.4:
resolution:
{
integrity: sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==,
}
engines: { node: ">=18.0.0" }
micromatch@4.0.8: micromatch@4.0.8:
resolution: resolution:
{ {
@@ -2546,6 +2745,12 @@ packages:
integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==, integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==,
} }
module-details-from-path@1.0.4:
resolution:
{
integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==,
}
ms@2.1.3: ms@2.1.3:
resolution: resolution:
{ {
@@ -3070,6 +3275,13 @@ packages:
} }
engines: { node: ">=0.10.0" } engines: { node: ">=0.10.0" }
require-in-the-middle@8.0.1:
resolution:
{
integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==,
}
engines: { node: ">=9.3.0 || >=8.10.0 <9.0.0" }
resolve-from@4.0.0: resolve-from@4.0.0:
resolution: resolution:
{ {
@@ -3106,6 +3318,12 @@ packages:
engines: { node: ^22.14.0 || >= 24.10.0 } engines: { node: ^22.14.0 || >= 24.10.0 }
hasBin: true hasBin: true
semifies@1.0.0:
resolution:
{
integrity: sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==,
}
semver-regex@4.0.5: semver-regex@4.0.5:
resolution: resolution:
{ {
@@ -3852,6 +4070,30 @@ snapshots:
"@jridgewell/gen-mapping": 0.3.13 "@jridgewell/gen-mapping": 0.3.13
"@jridgewell/trace-mapping": 0.3.31 "@jridgewell/trace-mapping": 0.3.31
"@apm-js-collab/code-transformer-bundler-plugins@0.7.4":
dependencies:
"@apm-js-collab/code-transformer": 0.18.1
es-module-lexer: 2.3.1
magic-string: 0.30.21
module-details-from-path: 1.0.4
"@apm-js-collab/code-transformer@0.18.1":
dependencies:
"@types/estree": 1.0.9
astring: 1.9.0
esquery: 1.7.0
meriyah: 6.1.4
semifies: 1.0.0
source-map: 0.6.1
"@apm-js-collab/tracing-hooks@0.13.0":
dependencies:
"@apm-js-collab/code-transformer": 0.18.1
debug: 4.4.3
module-details-from-path: 1.0.4
transitivePeerDependencies:
- supports-color
"@babel/code-frame@7.29.7": "@babel/code-frame@7.29.7":
dependencies: dependencies:
"@babel/helper-validator-identifier": 7.29.7 "@babel/helper-validator-identifier": 7.29.7
@@ -4126,6 +4368,49 @@ snapshots:
dependencies: dependencies:
"@octokit/openapi-types": 28.0.0 "@octokit/openapi-types": 28.0.0
"@opentelemetry/api-logs@0.220.0":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/api@1.9.1": {}
"@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1)":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/semantic-conventions": 1.43.0
"@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/api-logs": 0.220.0
import-in-the-middle: 3.3.3
require-in-the-middle: 8.0.1
transitivePeerDependencies:
- supports-color
"@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1)":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/core": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/semantic-conventions": 1.43.0
"@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/core": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/resources": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/sdk-trace": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/semantic-conventions": 1.43.0
"@opentelemetry/sdk-trace@2.10.0(@opentelemetry/api@1.9.1)":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/core": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/resources": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/semantic-conventions": 1.43.0
"@opentelemetry/semantic-conventions@1.43.0": {}
"@oxlint/binding-android-arm-eabi@1.77.0": "@oxlint/binding-android-arm-eabi@1.77.0":
optional: true optional: true
@@ -4369,6 +4654,58 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
"@sentry/conventions@0.16.0": {}
"@sentry/core@10.69.0":
dependencies:
"@sentry/conventions": 0.16.0
"@sentry/node-core@10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))":
dependencies:
"@sentry/conventions": 0.16.0
"@sentry/core": 10.69.0
"@sentry/opentelemetry": 10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))
import-in-the-middle: 3.3.3
optionalDependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/core": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/instrumentation": 0.220.0(@opentelemetry/api@1.9.1)
"@opentelemetry/sdk-trace-base": 2.10.0(@opentelemetry/api@1.9.1)
"@sentry/node@10.69.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/instrumentation": 0.220.0(@opentelemetry/api@1.9.1)
"@opentelemetry/sdk-trace-base": 2.10.0(@opentelemetry/api@1.9.1)
"@sentry/conventions": 0.16.0
"@sentry/core": 10.69.0
"@sentry/node-core": 10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))
"@sentry/opentelemetry": 10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))
"@sentry/server-utils": 10.69.0
import-in-the-middle: 3.3.3
transitivePeerDependencies:
- "@opentelemetry/core"
- "@opentelemetry/exporter-trace-otlp-http"
- supports-color
"@sentry/opentelemetry@10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))":
dependencies:
"@opentelemetry/api": 1.9.1
"@opentelemetry/core": 2.10.0(@opentelemetry/api@1.9.1)
"@opentelemetry/sdk-trace-base": 2.10.0(@opentelemetry/api@1.9.1)
"@sentry/conventions": 0.16.0
"@sentry/core": 10.69.0
"@sentry/server-utils@10.69.0":
dependencies:
"@apm-js-collab/code-transformer-bundler-plugins": 0.7.4
"@apm-js-collab/tracing-hooks": 0.13.0
"@sentry/conventions": 0.16.0
"@sentry/core": 10.69.0
meriyah: 6.1.4
transitivePeerDependencies:
- supports-color
"@simple-libs/stream-utils@1.2.0": {} "@simple-libs/stream-utils@1.2.0": {}
"@sindresorhus/is@4.6.0": {} "@sindresorhus/is@4.6.0": {}
@@ -4494,6 +4831,8 @@ snapshots:
estree-walker: 3.0.3 estree-walker: 3.0.3
js-tokens: 10.0.0 js-tokens: 10.0.0
astring@1.9.0: {}
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
balanced-match@4.0.4: {} balanced-match@4.0.4: {}
@@ -4552,6 +4891,8 @@ snapshots:
dependencies: dependencies:
readdirp: 4.1.2 readdirp: 4.1.2
cjs-module-lexer@2.2.0: {}
clean-stack@5.3.0: clean-stack@5.3.0:
dependencies: dependencies:
escape-string-regexp: 5.0.0 escape-string-regexp: 5.0.0
@@ -4700,6 +5041,8 @@ snapshots:
es-module-lexer@1.7.0: {} es-module-lexer@1.7.0: {}
es-module-lexer@2.3.1: {}
esbuild@0.27.7: esbuild@0.27.7:
optionalDependencies: optionalDependencies:
"@esbuild/aix-ppc64": 0.27.7 "@esbuild/aix-ppc64": 0.27.7
@@ -4764,6 +5107,12 @@ snapshots:
escape-string-regexp@5.0.0: {} escape-string-regexp@5.0.0: {}
esquery@1.7.0:
dependencies:
estraverse: 5.3.0
estraverse@5.3.0: {}
estree-walker@3.0.3: estree-walker@3.0.3:
dependencies: dependencies:
"@types/estree": 1.0.9 "@types/estree": 1.0.9
@@ -4957,6 +5306,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
import-in-the-middle@3.3.3:
dependencies:
cjs-module-lexer: 2.2.0
es-module-lexer: 2.3.1
module-details-from-path: 1.0.4
import-meta-resolve@4.2.0: {} import-meta-resolve@4.2.0: {}
indent-string@5.0.0: {} indent-string@5.0.0: {}
@@ -5121,6 +5476,8 @@ snapshots:
merge-stream@2.0.0: {} merge-stream@2.0.0: {}
meriyah@6.1.4: {}
micromatch@4.0.8: micromatch@4.0.8:
dependencies: dependencies:
braces: 3.0.3 braces: 3.0.3
@@ -5149,6 +5506,8 @@ snapshots:
pkg-types: 1.3.1 pkg-types: 1.3.1
ufo: 1.6.4 ufo: 1.6.4
module-details-from-path@1.0.4: {}
ms@2.1.3: {} ms@2.1.3: {}
mz@2.7.0: mz@2.7.0:
@@ -5398,6 +5757,13 @@ snapshots:
require-directory@2.1.1: {} require-directory@2.1.1: {}
require-in-the-middle@8.0.1:
dependencies:
debug: 4.4.3
module-details-from-path: 1.0.4
transitivePeerDependencies:
- supports-color
resolve-from@4.0.0: {} resolve-from@4.0.0: {}
resolve-from@5.0.0: {} resolve-from@5.0.0: {}
@@ -5471,6 +5837,8 @@ snapshots:
- supports-color - supports-color
- typescript - typescript
semifies@1.0.0: {}
semver-regex@4.0.5: {} semver-regex@4.0.5: {}
semver@7.8.5: {} semver@7.8.5: {}
+37 -6
View File
@@ -1,5 +1,11 @@
import type { LogEvent, LogLevel, LogSink } from "../types"; import type { LogEvent, LogLevel, LogSink } from "../types";
/**
* Sentry Issue severity levels (`@sentry/core` `SeverityLevel`).
* Note: Issues use `"warning"`; Logs use `"warn"`.
*/
export type SentrySeverityLevel = "fatal" | "error" | "warning" | "log" | "info" | "debug";
/** /**
* Options for {@link createSentrySink}. * Options for {@link createSentrySink}.
* *
@@ -23,13 +29,16 @@ export interface SentrySinkOptions {
/** /**
* Minimal Sentry scope surface required by {@link createSentrySink}. * Minimal Sentry scope surface required by {@link createSentrySink}.
* Satisfied by `@sentry/browser`, `@sentry/node`, `@sentry/nextjs`, etc. * Satisfied by `@sentry/browser`, `@sentry/node`, `@sentry/nextjs`, etc.
*
* Method parameter types are intentionally aligned with Sentry's public API
* (contravariance) so `typeof Sentry` assigns to {@link SentryLike} without adapters.
*/ */
export interface SentryScopeLike { export interface SentryScopeLike {
/** /**
* Sets the severity for the event about to be captured. * Sets the severity for the event about to be captured.
* @param level - Logger level string (e.g. `"error"`, `"info"`). * @param level - Sentry Issue severity (`"warning"`, not logger `"warn"`).
*/ */
setLevel(level: string): void; setLevel(level: SentrySeverityLevel): void;
/** /**
* Attaches a string tag to the event. * Attaches a string tag to the event.
* @param key - Tag name (this package uses `"logger.namespace"`). * @param key - Tag name (this package uses `"logger.namespace"`).
@@ -59,6 +68,8 @@ export interface SentryLoggerApiLike {
* Minimal Sentry client surface required by {@link createSentrySink}. * Minimal Sentry client surface required by {@link createSentrySink}.
* Pass your app's Sentry SDK module; this package does not depend on Sentry. * Pass your app's Sentry SDK module; this package does not depend on Sentry.
* *
* Structural typing matches official `@sentry/*` exports — no consumer adapter needed.
*
* @example * @example
* ```ts * ```ts
* import * as Sentry from "@sentry/nextjs"; * import * as Sentry from "@sentry/nextjs";
@@ -80,10 +91,11 @@ export interface SentryLike {
captureException(error: Error): void; captureException(error: Error): void;
/** /**
* Captures a string message when no `Error` is present in the arguments. * Captures a string message when no `Error` is present in the arguments.
* Second argument matches Sentry's optional severity / capture-context slot.
* @param message - Joined string arguments, or a fallback. * @param message - Joined string arguments, or a fallback.
* @param level - Event severity. * @param captureContext - Issue severity (use {@link toSentrySeverity} mapping).
*/ */
captureMessage(message: string, level: string): void; captureMessage(message: string, captureContext?: SentrySeverityLevel): void | string;
/** Structured Logs API. Required when using `{ logs: true }` or `{ sentry: true }`. */ /** Structured Logs API. Required when using `{ logs: true }` or `{ sentry: true }`. */
logger?: SentryLoggerApiLike; logger?: SentryLoggerApiLike;
} }
@@ -103,6 +115,21 @@ export function isSentrySink(sink: LogSink): sink is SentrySink {
return "kind" in sink && (sink as { kind?: unknown }).kind === "sentry"; return "kind" in sink && (sink as { kind?: unknown }).kind === "sentry";
} }
/**
* Maps a logger level to a Sentry **Issue** {@link SentrySeverityLevel}.
*
* - `"warn"` → `"warning"` (Issues API; Logs keep `"warn"`)
* - `"trace"` → `"debug"` (closest Issue severity)
* - other levels map by name (`error`, `info`, `debug`)
*
* @param level - Logger emit level (never `silent`).
*/
export function toSentrySeverity(level: Exclude<LogLevel, "silent">): SentrySeverityLevel {
if (level === "warn") return "warning";
if (level === "trace") return "debug";
return level;
}
function isAttributeValue(value: unknown): value is string | number | boolean { function isAttributeValue(value: unknown): value is string | number | boolean {
return typeof value === "string" || typeof value === "number" || typeof value === "boolean"; return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
} }
@@ -143,6 +170,9 @@ export function toSentryLogPayload(event: LogEvent): {
* event with `{ sentry: true }` on the log call. Suppress an Issue with * event with `{ sentry: true }` on the log call. Suppress an Issue with
* `{ suppressSentry: true }`. * `{ suppressSentry: true }`.
* *
* Official `@sentry/node`, `@sentry/browser`, and `@sentry/nextjs` modules are
* assignable to {@link SentryLike} directly — no adapter object required.
*
* @param sentry - Any object implementing {@link SentryLike} (typically the Sentry SDK). * @param sentry - Any object implementing {@link SentryLike} (typically the Sentry SDK).
* @param options - Optional Logs configuration. * @param options - Optional Logs configuration.
* @returns A sink to pass as `sentry` or inside `sinks` on {@link createLogger}. * @returns A sink to pass as `sentry` or inside `sinks` on {@link createLogger}.
@@ -174,12 +204,13 @@ export function createSentrySink(sentry: SentryLike, options: SentrySinkOptions
if (event.sendToSentryIssue) { if (event.sendToSentryIssue) {
const error = event.arguments.find((item): item is Error => item instanceof Error); const error = event.arguments.find((item): item is Error => item instanceof Error);
const { message, attributes } = toSentryLogPayload(event); const { message, attributes } = toSentryLogPayload(event);
const severity = toSentrySeverity(event.level);
sentry.withScope((scope) => { sentry.withScope((scope) => {
scope.setLevel(event.level); scope.setLevel(severity);
if (event.namespace) scope.setTag("logger.namespace", event.namespace); if (event.namespace) scope.setTag("logger.namespace", event.namespace);
scope.setExtras(attributes); scope.setExtras(attributes);
if (error) sentry.captureException(error); if (error) sentry.captureException(error);
else sentry.captureMessage(message, event.level); else sentry.captureMessage(message, severity);
}); });
return; return;
} }
+30
View File
@@ -0,0 +1,30 @@
/**
* Compile-time regression: official Sentry SDKs must assign to {@link SentryLike}
* without casts or consumer adapters. Checked by `pnpm check` (`tsc --noEmit`).
*/
import * as Sentry from "@sentry/node";
import { createSentrySink, type SentryLike } from "../src/sentry";
const _sdk: SentryLike = Sentry;
createSentrySink(Sentry);
createSentrySink(Sentry, { logs: true });
const stub: SentryLike = {
withScope: (callback) =>
callback({
setLevel: () => undefined,
setTag: () => undefined,
setExtras: () => undefined,
}),
captureException: () => undefined,
captureMessage: () => undefined,
logger: {
trace: () => undefined,
debug: () => undefined,
info: () => undefined,
warn: () => undefined,
error: () => undefined,
},
};
createSentrySink(stub);
createSentrySink(stub, { logs: true });
+67 -2
View File
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from "vitest"; import { describe, expect, it, vi } from "vitest";
import { createLogger } from "../src/index"; import { createLogger, type LogEvent } from "../src/index";
import { createSentrySink, toSentryLogPayload } from "../src/sentry"; import { createSentrySink, toSentryLogPayload, toSentrySeverity } from "../src/sentry";
function createFakeSentry() { function createFakeSentry() {
const scope = { setLevel: vi.fn(), setTag: vi.fn(), setExtras: vi.fn() }; const scope = { setLevel: vi.fn(), setTag: vi.fn(), setExtras: vi.fn() };
@@ -21,6 +21,29 @@ function createFakeSentry() {
}; };
} }
function issueEvent(overrides: Partial<LogEvent> = {}): LogEvent {
return {
level: "error",
arguments: ["message"],
timestamp: new Date(),
environment: "production",
sendToSentryLogs: false,
sendToSentryIssue: true,
sendToConsole: false,
...overrides,
};
}
describe("toSentrySeverity", () => {
it("maps logger levels to Sentry Issue severities", () => {
expect(toSentrySeverity("warn")).toBe("warning");
expect(toSentrySeverity("trace")).toBe("debug");
expect(toSentrySeverity("error")).toBe("error");
expect(toSentrySeverity("info")).toBe("info");
expect(toSentrySeverity("debug")).toBe("debug");
});
});
describe("toSentryLogPayload", () => { describe("toSentryLogPayload", () => {
it("joins string messages and flattens primitive object attributes", () => { it("joins string messages and flattens primitive object attributes", () => {
expect( expect(
@@ -111,4 +134,46 @@ describe("createSentrySink", () => {
expect(sentry.captureException).not.toHaveBeenCalled(); expect(sentry.captureException).not.toHaveBeenCalled();
expect(sentry.logger.warn).not.toHaveBeenCalled(); expect(sentry.logger.warn).not.toHaveBeenCalled();
}); });
it("maps warn → warning on the Issues path and keeps warn on the Logs path", () => {
const { scope, sentry } = createFakeSentry();
const sink = createSentrySink(sentry, { logs: true });
sink.emit(
issueEvent({
level: "warn",
arguments: ["degraded"],
}),
);
expect(scope.setLevel).toHaveBeenCalledWith("warning");
expect(sentry.captureMessage).toHaveBeenCalledWith("degraded", "warning");
expect(sentry.logger.warn).not.toHaveBeenCalled();
sink.emit({
level: "warn",
namespace: "API",
arguments: ["slow", { ms: 10 }],
timestamp: new Date(),
environment: "production",
sendToSentryLogs: true,
sendToSentryIssue: false,
sendToConsole: false,
});
expect(sentry.logger.warn).toHaveBeenCalledWith("slow", {
"logger.namespace": "API",
ms: 10,
});
});
it("maps error Issue severity as error", () => {
const { scope, sentry } = createFakeSentry();
createSentrySink(sentry).emit(
issueEvent({
level: "error",
arguments: ["failed"],
}),
);
expect(scope.setLevel).toHaveBeenCalledWith("error");
expect(sentry.captureMessage).toHaveBeenCalledWith("failed", "error");
});
}); });