Skip to content

sf2loki configuration reference

Config

FieldTypeDefaultRequiredDescription
salesforceSalesforceConfignoSingle-org Salesforce connection and authentication. Set this OR orgs (exactly one). Omit when using the multi-org orgs list.
orgslist[OrgConfig]noMulti-org list: ingest several Salesforce orgs from one process into one shared sink. Set this OR top-level salesforce (exactly one). Each entry carries its own salesforce + sources; the sink/state/service stay shared.
sourcesSourcesConfignoSingle-org event source selection and settings. Ignored (and rejected if customized) when orgs is used — put per-org sources under each org.
sinkSinkConfigyesLog sink settings.
stateStateConfignoCheckpoint/state store settings.
coordinateCoordinateConfignoLeadership coordination for active-passive HA.
serviceServiceConfignoApplication-level service settings.

SalesforceConfig

FieldTypeDefaultRequiredDescription
login_urlstr""noOptional: derived from environment when omitted; set to a custom My Domain URL to override. An explicit value always takes precedence over the environment-derived default.
environmentLiteral['production', 'sandbox']productionnoproduction | sandbox — derives login_url when login_url is unset; an explicit login_url takes precedence.
auth_modeLiteral['jwt_bearer', 'client_credentials']jwt_bearernojwt_bearer (private key + cert) | client_credentials (consumer key + secret).
client_idstr${SF_CLIENT_ID}yesExternal Client App consumer key.
client_secretSecretStr(secret)noclient_credentials flow secret (injectable from file/env like the key). Required when auth_mode is client_credentials; unused for jwt_bearer.
client_secret_filePath(secret)noFile path to the client_credentials secret; required when auth_mode: client_credentials.
usernamestrsvc@example.comnoIntegration user (pre-authorised on the app's Policies tab). Required for jwt_bearer (the JWT sub claim); not needed when auth_mode: client_credentials.
private_key_filePath(secret)noFile path to the jwt_bearer private key; not needed when auth_mode: client_credentials.
private_keySecretStr(secret)nojwt_bearer private key, inline (alternative to private_key_file).
api_versionstr"60.0"noSalesforce REST/SOAP API version to target.
org_idstrnullnoSet this to keep the app on the api scope alone; left null it is auto-resolved via /services/oauth2/userinfo (which then needs the openid scope).
token_ttlDuration1hnoAssumed access-token lifetime (Salesforce returns no expires_in for these flows; the real lifetime is the org's session timeout, which can be as short as 15m). Refresh is also reactive on 401, so this only tunes proactive re-mint cadence.
limitsSalesforceLimitsConfignoOrg-limits metric poller settings.

SalesforceLimitsConfig

FieldTypeDefaultRequiredDescription
enabledboolfalsenoPoll /services/data/vXX.0/limits for org-limit gauges (API usage, storage, streaming events, ...).
poll_intervalDuration5mnoHow often to poll the limits endpoint.

OrgConfig

FieldTypeDefaultRequiredDescription
namestrprodyesOrg identifier: becomes the org stream label and the checkpoint key prefix. Letters, digits, underscore, hyphen only; must be unique.
salesforceSalesforceConfigyesThis org's Salesforce connection and authentication.
sourcesSourcesConfignoThis org's event source selection and settings.

SourcesConfig

FieldTypeDefaultRequiredDescription
pubsubPubSubConfignoPub/Sub API streaming source.
eventlog_objectsEventLogObjectsConfignoEvent-object SOQL polling source.
eventlogfileEventLogFileConfignoEventLogFile (CSV) ingestion source.
apexlogApexLogConfignoApexLog (Tooling API debug log) polling source.
allow_overlapboolfalsenoBypass the fail-fast overlap guard that refuses to start when one event category is enabled on more than one source.
transform_saltSecretStr(secret)noDeployment-wide salt for hash transform rules (stable pseudonyms that still correlate within this deployment). Strongly recommended whenever a hash rule is configured — unsalted hashes of low-entropy values (IPs, usernames) are trivially reversible by table lookup.
transform_salt_filePath/etc/sf2loki/secrets/transform-saltnoFile path to the transform hash salt (alternative to transform_salt).

PubSubConfig

FieldTypeDefaultRequiredDescription
enabledbooltruenoEnable the Pub/Sub streaming source.
endpointstrapi.pubsub.salesforce.com:7443noPub/Sub API gRPC endpoint.
default_num_requestedint100noFlow-control batch size (1-100; Salesforce clamps at 100 and returns INVALID_ARGUMENT when over-asked).
replay_presetLiteral['LATEST', 'EARLIEST', 'CUSTOM']CUSTOMnoReplay position; falls back to LATEST when no stored replay_id.
topicslist[str][/event/LoginEventStream, /event/ApiAnomalyEvent]noExplicit topics, or "*" to DISCOVER and subscribe to every RTEM stream the org exposes (the *EventStream channels), re-filtered by include/exclude.
includelist[str]["*"]noOperator inclusion globs applied to discovered/explicit topics.
excludelist[str][]noOperator exclusion globs applied to discovered/explicit topics.
rediscovery_intervalDuration1hnoHow often to re-run wildcard ("*") topic discovery while running, so channels enabled after startup are picked up without a restart. 0 disables (discovery then runs only at startup).
sampledict[str, typing.Annotated[float, FieldInfo(annotation=NoneType, required=True, metadata=[Gt(gt=0.0), Le(le=1.0)])]]{/event/ApiEventStream: 0.25}noOpt-in lossy volume control: topic glob -> keep fraction (0-1], first matching glob wins. Sampling is deterministic by replay_id hash, so a replay keeps exactly the same rows (Loki dedup stays intact). Sampled-out events still advance checkpoints.
transformslist[TransformRule]noRedaction/filter rules applied to each decoded event payload before shaping (see TransformRule).
bridge_max_bytesint134217728noApproximate byte budget for the Pub/Sub source's internal topic->events bridge queue, which sits UPSTREAM of the pipeline's sink.loki.batch.queue_max_bytes. Topic tasks block (structural backpressure, propagated to flow-control credits) once the bridged-but-undrained bytes exceed this, so a sink outage can't balloon per-org buffering past the count bound. Default 128 MiB; 0 disables byte accounting on the bridge.

TransformRule

FieldTypeDefaultRequiredDescription
actionLiteral['hash', 'mask', 'drop_field', 'drop_row', 'regex_replace']nullyeshash (salted SHA-256 -> stable pseudonym) | mask (format-aware: emails keep the domain, IPv4 truncates to /24, else '***') | drop_field | drop_row (row filter via match) | regex_replace (pattern -> replacement).
fieldslist[str][SOURCE_IP, CLIENT_IP]noPayload field names the action applies to (required for hash/mask/drop_field/regex_replace; not used by drop_row).
matchdict[str, str]{}nodrop_row only: drop rows where EVERY field equals (or glob-matches) its value, e.g. {EVENT_TYPE: Sites}.
patternstrnullnoregex_replace only: the regular expression to replace (must compile).
replacementstr""noregex_replace only: the replacement text (backrefs allowed).
namestr""noOptional stable rule name used as the rule metric label for drop_row counts; defaults to "-".

EventLogObjectsConfig

FieldTypeDefaultRequiredDescription
enabledboolfalsenoEnable the event-object polling source.
objectslist[EventLogObjectConfig]noEvent objects to poll.
transformslist[TransformRule]noRedaction/filter rules applied to each polled record before shaping (see TransformRule).

EventLogObjectConfig

FieldTypeDefaultRequiredDescription
namestr""yesThe event object API name to poll (e.g. LoginEvent).
timestamp_fieldstrEventDatenoThe field used as the per-row event time for polling/checkpointing.
poll_intervalDuration5mnoHow often to poll this object.
lookbackDuration1hnoInitial window to fetch on first run (no checkpoint).
samplefloat1.0noOpt-in lossy volume control: keep fraction (0-1] of rows, deterministic by record Id hash (replay-stable). 1.0 keeps everything.
big_objectboolfalsenoSet true for Salesforce Big Objects (the stored RTEM event family: LoginEvent, ApiEvent, FileEventStore, *EventStore, ...). Big Objects reject ORDER BY ASC, so the source drains them newest-first (ORDER BY timestamp_field DESC) with a ratcheting upper bound and re-sorts each cycle's window ascending before emitting. Leave false for standard and custom objects (LoginHistory, MyAudit__c), which use the ASC path.
max_catchup_recordsint200000noCap on records collected into memory per big_object DESC drain cycle. The drain buffers a cycle's window in memory to re-sort it ascending; a post-outage catch-up over a large gap would otherwise be unbounded and can OOM. When the cap is hit the drain emits that (internally sorted) segment and ratchets its upper bound down so catch-up proceeds in bounded chunks across cycles. 0 = unbounded (pre-cap behaviour). Ignored on the ASC path (streamed page-by-page, already bounded).

EventLogFileConfig

FieldTypeDefaultRequiredDescription
enabledboolfalsenoEnable the EventLogFile ingestion source.
intervalLiteral['Hourly', 'Daily']HourlynoDaily (settled, ~1d lag) | Hourly (fresher, needs the Event Monitoring hourly opt-in in Setup) — pick ONE. Many orgs generate ONLY Daily files; check before Hourly.
event_typeslist[EventLogFileTypeConfig]noELF EventTypes to ingest (required when enabled — there is no sensible "all" default given ~70 types and the either/or-per-category model). Each item is a bare string (e.g. "Login") or a per-type object (name + optional structured_metadata_fields/labels). Use "*" to discover and ingest every EventType the org produces for this interval.
excludelist[str][]noEventTypes to skip when the wildcard "*" is used (e.g. a category served by another source, or a high-volume type you don't want). Ignored otherwise.
poll_intervalDuration1hnoHow often to list new files (Daily files land ~once/day).
lookbackDuration1dnoInitial window on first run (no checkpoint); reach back far enough to catch the last few settled Daily files.
timestamp_columnstrTIMESTAMP_DERIVEDnoPer-row event time column.
page_sizeint1000noSOQL LIMIT for the file-listing query.
settle_windowDuration0snoSkip files whose CreatedDate is newer than now-settle_window, so we don't pull a half-written hourly CSV whose tail rows would then be skipped when the watermark passes it. Left unset it defaults to 5m for interval: Hourly (Hourly blobs can be listed while server-side incomplete) and 0 for Daily (files land long after the day closes). Set explicitly to override either.
download_max_ageDuration1dnoA file whose body keeps failing to download and is older than this is abandoned (checkpoint advances past it) so a permanently-missing file can't wedge the watermark forever. Files younger than this are retried.
transformslist[TransformRule]noRedaction/filter rules applied to each CSV row before shaping (see TransformRule).
concurrencyint4noEvent types processed concurrently per poll cycle (per-type ordering and checkpoints are unaffected — types are independent). Peak memory is roughly concurrency x 8 MiB of download spool.

EventLogFileTypeConfig

FieldTypeDefaultRequiredDescription
namestr""yesThe ELF EventType this override applies to (e.g. ReportExport), or "*" to discover all types.
structured_metadata_fieldslist[str][REPORT_ID, OWNER_ID]noPer-type override of the global sink.loki.structured_metadata_fields; omit (None) to inherit the global list, or set to [] to suppress it.
labelslist[str][DELEGATED_USER]noColumns promoted to Loki stream labels for this event type. Keep these LOW cardinality — each distinct value is a new Loki stream.
samplefloat1.0noOpt-in lossy volume control: keep fraction (0-1] of this type's rows, deterministic by row key hash (replay-stable, Loki-dedup-safe). 1.0 keeps everything.

ApexLogConfig

FieldTypeDefaultRequiredDescription
enabledboolfalsenoEnable the ApexLog polling source.
poll_intervalDuration1mnoHow often to poll for new ApexLog rows.
lookbackDuration1hnoInitial window to fetch on first run (no checkpoint).
userslist[str][integration@example.com]noSalesforce usernames whose logs to ingest (matched via LogUser.Username). Empty = every ApexLog visible to the integration user.
max_body_bytesint5242880noSkip the body download for logs whose LogLength exceeds this (the metadata line is still shipped, flagged body_skipped); the per-line cap (sink.loki.batch.max_line_bytes) truncates whatever is shipped.
samplefloat1.0noOpt-in lossy volume control: keep fraction (0-1] of logs, deterministic by log Id hash (replay-stable). 1.0 keeps everything.
transformslist[TransformRule]noRedaction/filter rules applied to each log's metadata before shaping.

SinkConfig

FieldTypeDefaultRequiredDescription
lokiLokiConfigyesLoki sink settings.

LokiConfig

FieldTypeDefaultRequiredDescription
urlstr${GC_LOKI}yesLoki push API URL.
tenant_idstr${GC_TENANT_ID}noLoki tenant (X-Scope-OrgID); required for Grafana Cloud and multi-tenant Loki.
auth_token_filePath(secret)noFile path to the Loki auth token.
auth_tokenSecretStr(secret)noLoki auth token, inline (alternative to auth_token_file).
encodingLiteral['protobuf', 'json']protobufnoWire encoding for the push request.
compressionLiteral['snappy', 'gzip', 'none']snappynoCompression: snappy (protobuf) | gzip (json) | none.
batchLokiBatchConfignoPush batching settings.
egressEgressConfignoEgress guardrails: rate caps + daily byte budget (all off by default).
labelsdict[str, str]{environment: prod}noStatic stream labels merged onto every push (job + sf_org_id are added automatically).
structured_metadata_fieldslist[str][replay_id, schema_id, event_uuid, user_id, username, source_ip, session_key]noEvent fields promoted to Loki structured metadata (not stream labels).

LokiBatchConfig

FieldTypeDefaultRequiredDescription
max_entriesint1000noFlush the batch after this many log entries.
max_bytesint1048576noFlush the batch after this many bytes.
flush_intervalDuration1snoFlush the batch after this much time, regardless of size.
max_line_bytesint262144noPer-line UTF-8 byte cap; lines longer than this are truncated (with a marker) before push so one oversized row can't 400 its whole batch. Mirrors Loki's server-side max_line_size default (256 KiB). 0 disables.
queue_maxsizeint10000noEntry-count bound of each internal source->sink lane queue. Producers block (structural backpressure) when full. Applied PER LANE (streaming vs bulk), so the worst-case entry count is queue_maxsize x number-of-lanes (<= 2).
queue_max_bytesint268435456noApproximate byte budget for queued entries, applied PER LANE (streaming vs bulk). Producers on a lane block when its budget is exceeded, even if the entry-count bound is not reached. Worst-case buffered memory during a sink outage is therefore queue_max_bytes x number-of-lanes (<= 2x). Default 256 MiB; 0 disables accounting.

EgressConfig

FieldTypeDefaultRequiredDescription
max_lines_per_secondfloat0noToken-bucket cap on pushed lines/second; 0 disables.
max_bytes_per_secondfloat0noToken-bucket cap on pushed (pre-compression) bytes/second; 0 disables.
daily_byte_budgetint0noMaximum pre-compression bytes pushed per UTC day; 0 disables. The used counter persists in the state store, so restarts don't reset it. WARN at 80%, ERROR + budget_action at 100%.
budget_actionLiteral['pause', 'drop']pausenoWhat to do when the daily budget is exhausted: pause (hold pushes + checkpoints until the next UTC day; data delayed, never lost, readiness reports degraded) | drop (keep running, discard over-budget entries).

StateConfig

FieldTypeDefaultRequiredDescription
storeLiteral['file', 's3', 'gcs']filenoState backend: file (local JSON, needs a persistent volume) | s3 (S3-compatible object storage, for stateless deployments; needs the sf2loki[s3] extra) | gcs (Google Cloud Storage, for stateless deployments; needs the sf2loki[gcs] extra).
fileFileStateConfignoFile-backed state store settings.
s3S3StateConfignoS3-backed state store settings.
gcsGcsStateConfignoGCS-backed state store settings.

FileStateConfig

FieldTypeDefaultRequiredDescription
pathPath/var/lib/sf2loki/state.jsonnoCheckpoint file path; persist on a mounted volume for durable resume.

S3StateConfig

FieldTypeDefaultRequiredDescription
bucketstr""noBucket name (required when state.store is s3).
keystrsf2loki/state.jsonnoObject key holding the checkpoint document.
regionstrnullnoAWS region; omit to use the default-chain region.
endpoint_urlstrhttp://minio:9000noCustom S3 endpoint for MinIO/R2/Ceph; omit for AWS S3.

GcsStateConfig

FieldTypeDefaultRequiredDescription
bucketstr""noBucket name (required when state.store is gcs).
object_namestrsf2loki/state.jsonnoObject name holding the checkpoint document.
service_filePathnullnoPath to a service-account JSON key; omit to use Application Default Credentials.

CoordinateConfig

FieldTypeDefaultRequiredDescription
typeLiteral['noop', 'file_lease', 'k8s_lease']noopnonoop (single instance) | file_lease (active-passive via a shared lease file) | k8s_lease (active-passive via a Kubernetes Lease; needs the sf2loki[k8s] extra).
file_leaseFileLeaseConfignoFile-lease coordinator settings.
k8s_leaseK8sLeaseConfignoKubernetes-Lease coordinator settings.

FileLeaseConfig

FieldTypeDefaultRequiredDescription
pathPath/var/lib/sf2loki/leader.leasenoLease file path on storage shared by all replicas.
ttlDuration30snoLease lifetime: a standby takes over once the lease is this stale. Failover time is bounded by ttl; must exceed inter-host clock skew.
renew_intervalDuration10snoHow often the leader re-writes the lease (must be < ttl/2).
holder_idstr""noStable identity written into the lease; defaults to hostname-pid at startup. Set explicitly when hostnames aren't unique.

K8sLeaseConfig

FieldTypeDefaultRequiredDescription
namespacestrdefaultnoNamespace holding the Lease object.
namestrsf2loki-leadernoLease object name (shared by all replicas).
identitystr""noholderIdentity written into the Lease; defaults to the pod name ($HOSTNAME) at startup.
lease_durationDuration30snoLease lifetime: a standby takes over once the lease is this stale. Failover time is bounded by this.
renew_intervalDuration10snoHow often the leader renews the Lease (must be < lease_duration/2).
kubeconfigPathnullnoPath to a kubeconfig for out-of-cluster dev; omit to use in-cluster config.

ServiceConfig

FieldTypeDefaultRequiredDescription
log_levelLiteral['debug', 'info', 'warning', 'warn', 'error', 'critical']infonoApplication log level: debug | info | warning | error | critical (case-insensitive).
log_formatLiteral['json', 'logfmt']jsonnoApplication log output format.
health_addrstr":8080"noAddress to bind the health-check HTTP server.
shutdown_graceDuration25snoGrace period allowed for in-flight work to finish on shutdown.
unready_after_sink_failingDuration15mno/readyz reports 503 when Loki pushes have been failing continuously for this long (data is retried and safe, but the instance is degraded and an orchestrator should surface it). 0 disables the readiness degradation.
telemetryTelemetryConfignoOTLP metrics egress settings.

TelemetryConfig

FieldTypeDefaultRequiredDescription
enabledboolfalsenoPush all metrics via OTLP/HTTP.
endpointstrhttps://otlp-gateway-.grafana.net/otlp/v1/metrics no Full OTLP/HTTP metrics URL. For Grafana Cloud this is the stack OTLP gateway, e.g. https://otlp-gateway-.grafana.net/otlp/v1/metrics; for a local Alloy otelcol.receiver.otlp, e.g. http://alloy:4318/v1/metrics.
authLiteral['basic', 'none']basicnoAuth for the OTLP endpoint. "basic" sends Authorization: Basic base64(user:token); "none" sends none (e.g. in-cluster Alloy). For basic, the credentials default to the Loki sink's tenant_id/auth_token when left blank — Grafana Cloud uses one stack credential for both Loki and OTLP.
basic_auth_userstr""noBasic-auth username; defaults to the Loki sink's tenant_id when blank.
basic_auth_tokenSecretStr(secret)noBasic-auth token, inline; defaults to the Loki sink's auth_token when blank.
basic_auth_token_filePath(secret)noFile path to the basic-auth token (alternative to basic_auth_token).
headersdict[str, str]{}noExplicit headers, merged on top of any computed Authorization header. Values support ${ENV} interpolation at config load.
export_intervalDuration1mnoHow often to export accumulated metrics via OTLP.
resource_attributesdict[str, str]{}noExtra OTel resource attributes merged onto the defaults (service.name, etc.).