
Research
/Security News
PolinRider Spreads Through Compromised GitHub Accounts and Packagist
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.
@sylphx/cat-tail-sampling
Advanced tools
Tail-based sampling plugin for @sylphx/cat logger
1.82 KB • 40-90% cost reduction • 100% error coverage • Adaptive budgeting
npm install @sylphx/cat @sylphx/cat-tracing @sylphx/cat-tail-sampling
Note: Requires @sylphx/cat-tracing for trace ID support.
Intelligent sampling that makes decisions AFTER trace completion based on full context. Keep 100% of errors while sampling routine logs, reducing observability costs by 40-90% without losing critical data. Features rule-based sampling, adaptive budget control, and session-based buffering.
Perfect for high-volume production systems where every error matters but routine logs can be sampled.
import { createLogger } from '@sylphx/cat'
import { tracingPlugin } from '@sylphx/cat-tracing'
import { tailSamplingPlugin } from '@sylphx/cat-tail-sampling'
const logger = createLogger({
plugins: [
tracingPlugin(),
tailSamplingPlugin({
rules: [
{
name: 'Keep all errors',
condition: (trace) => trace.metadata.hasError,
sampleRate: 1.0, // 100%
priority: 100
},
{
name: 'Sample normal traffic',
condition: () => true,
sampleRate: 0.1, // 10%
priority: 0
}
]
})
]
})
logger.info('Normal request') // 10% chance of being kept
logger.error('Failed request') // Always kept
import { tailSamplingPlugin } from '@sylphx/cat-tail-sampling'
const logger = createLogger({
plugins: [
tracingPlugin(),
tailSamplingPlugin({
adaptive: true,
monthlyBudget: 100_000_000, // 100 MB per month
rules: [
{
name: 'Keep all errors',
condition: (trace) => trace.metadata.hasError,
sampleRate: 1.0
},
{
name: 'Sample normal traffic',
condition: () => true,
sampleRate: 0.2 // Starting rate, adjusted automatically
}
]
})
]
})
import { tailSamplingPlugin } from '@sylphx/cat-tail-sampling'
const logger = createLogger({
plugins: [
tracingPlugin(),
tailSamplingPlugin({
rules: [
{
name: 'Keep all errors',
condition: (trace) => trace.metadata.hasError,
sampleRate: 1.0,
priority: 100
},
{
name: 'Keep slow requests (>1s)',
condition: (trace) => {
const duration = (trace.metadata.endTime || Date.now()) - trace.metadata.startTime
return duration > 1000
},
sampleRate: 1.0,
priority: 90
},
{
name: 'Sample fast requests',
condition: () => true,
sampleRate: 0.05, // 5%
priority: 0
}
]
})
]
})
tailSamplingPlugin(options?: TailSamplingPluginOptions): PluginCreates a tail-based sampling plugin.
Options:
enabled?: boolean - Enable tail sampling (default: true)rules?: SamplingRule[] - Sampling rules evaluated in priority ordermaxBufferSize?: number - Max logs per trace (default: 1000)maxTraceDuration?: number - Max trace duration in ms before auto-flush (default: 30000)adaptive?: boolean - Enable adaptive budget-aware sampling (default: false)monthlyBudget?: number - Monthly budget in bytes for adaptive samplinggetTraceId?: (entry: LogEntry) => string | undefined - Custom trace ID extractor (default: uses entry.traceId)onFlush?: (trace: TraceBuffer, kept: boolean) => void - Callback when trace is flushedSamplingRuleRule for determining whether to keep a trace.
Properties:
name?: string - Rule name for debuggingcondition: (trace: TraceBuffer) => boolean - Condition functionsampleRate: number - Sample rate from 0.0 (discard all) to 1.0 (keep all)priority?: number - Priority (higher = evaluated first, default: 0)TraceBufferBuffer containing all logs for a single trace.
Properties:
traceId: string - Trace IDlogs: LogEntry[] - All log entries in the tracemetadata: TraceMetadata - Aggregated trace metadataTraceMetadataMetadata aggregated from all logs in a trace.
Properties:
traceId: string - Trace IDstartTime: number - Trace start time (ms)endTime?: number - Trace end time (ms)logCount: number - Number of logs in tracehasError: boolean - True if any error/fatal logsmaxLevel: number - Highest log level (numeric)minDuration?: number - Minimum operation durationmaxDuration?: number - Maximum operation durationavgDuration?: number - Average operation durationstatusCode?: number - HTTP status code (if present)customFields: Record<string, unknown> - Custom metadatatailSamplingPlugin({
rules: [
{
name: 'Keep 5xx errors',
condition: (trace) => {
const status = trace.metadata.statusCode
return status !== undefined && status >= 500
},
sampleRate: 1.0,
priority: 100
},
{
name: 'Keep 4xx errors',
condition: (trace) => {
const status = trace.metadata.statusCode
return status !== undefined && status >= 400
},
sampleRate: 0.5, // 50%
priority: 90
},
{
name: 'Sample 2xx/3xx',
condition: () => true,
sampleRate: 0.05, // 5%
priority: 0
}
]
})
tailSamplingPlugin({
rules: [
// Tier 1: Always keep (priority 100)
{
name: 'Errors',
condition: (trace) => trace.metadata.hasError,
sampleRate: 1.0,
priority: 100
},
// Tier 2: High-value traces (priority 50)
{
name: 'Slow requests',
condition: (trace) => {
const duration = (trace.metadata.endTime || Date.now()) - trace.metadata.startTime
return duration > 2000
},
sampleRate: 0.8, // 80%
priority: 50
},
{
name: 'High log volume',
condition: (trace) => trace.metadata.logCount > 20,
sampleRate: 0.6, // 60%
priority: 45
},
// Tier 3: Sample everything else (priority 0)
{
name: 'Normal traffic',
condition: () => true,
sampleRate: 0.1, // 10%
priority: 0
}
]
})
tailSamplingPlugin({
getTraceId: (entry) => {
// Extract from custom field
return entry.data?.customTraceId || entry.traceId
},
rules: [...]
})
tailSamplingPlugin({
onFlush: (trace, kept) => {
console.log(`Trace ${trace.traceId}: ${kept ? 'KEPT' : 'DROPPED'}`)
console.log(` Logs: ${trace.metadata.logCount}`)
console.log(` HasError: ${trace.metadata.hasError}`)
console.log(` Duration: ${(trace.metadata.endTime || Date.now()) - trace.metadata.startTime}ms`)
},
rules: [...]
})
100 MB/month budget:
monthlyBudget: 100_000_000 // 100 MB
1 GB/month budget:
monthlyBudget: 1_000_000_000 // 1 GB
The adaptive sampler automatically adjusts sample rates to stay within budget while maintaining 100% error coverage.
onFlush callback to track sampling decisionsMIT © Kyle Zhu
FAQs
Tail-based sampling plugin for @sylphx/cat logger
The npm package @sylphx/cat-tail-sampling receives a total of 4 weekly downloads. As such, @sylphx/cat-tail-sampling popularity was classified as not popular.
We found that @sylphx/cat-tail-sampling demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.

Company News
Allow myself to introduce... myself.