diff --git a/docs/docset.yml b/docs/docset.yml
new file mode 100644
index 0000000..8dddbaf
--- /dev/null
+++ b/docs/docset.yml
@@ -0,0 +1,489 @@
+project: 'ECS Logging Node.js'
+cross_links:
+ - apm-agent-nodejs
+ - beats
+ - docs-content
+ - ecs
+ - ecs-logging
+toc:
+ - toc: reference
+subs:
+ ref: "https://www.elastic.co/guide/en/elasticsearch/reference/current"
+ ref-bare: "https://www.elastic.co/guide/en/elasticsearch/reference"
+ ref-8x: "https://www.elastic.co/guide/en/elasticsearch/reference/8.1"
+ ref-80: "https://www.elastic.co/guide/en/elasticsearch/reference/8.0"
+ ref-7x: "https://www.elastic.co/guide/en/elasticsearch/reference/7.17"
+ ref-70: "https://www.elastic.co/guide/en/elasticsearch/reference/7.0"
+ ref-60: "https://www.elastic.co/guide/en/elasticsearch/reference/6.0"
+ ref-64: "https://www.elastic.co/guide/en/elasticsearch/reference/6.4"
+ xpack-ref: "https://www.elastic.co/guide/en/x-pack/6.2"
+ logstash-ref: "https://www.elastic.co/guide/en/logstash/current"
+ kibana-ref: "https://www.elastic.co/guide/en/kibana/current"
+ kibana-ref-all: "https://www.elastic.co/guide/en/kibana"
+ beats-ref-root: "https://www.elastic.co/guide/en/beats"
+ beats-ref: "https://www.elastic.co/guide/en/beats/libbeat/current"
+ beats-ref-60: "https://www.elastic.co/guide/en/beats/libbeat/6.0"
+ beats-ref-63: "https://www.elastic.co/guide/en/beats/libbeat/6.3"
+ beats-devguide: "https://www.elastic.co/guide/en/beats/devguide/current"
+ auditbeat-ref: "https://www.elastic.co/guide/en/beats/auditbeat/current"
+ packetbeat-ref: "https://www.elastic.co/guide/en/beats/packetbeat/current"
+ metricbeat-ref: "https://www.elastic.co/guide/en/beats/metricbeat/current"
+ filebeat-ref: "https://www.elastic.co/guide/en/beats/filebeat/current"
+ functionbeat-ref: "https://www.elastic.co/guide/en/beats/functionbeat/current"
+ winlogbeat-ref: "https://www.elastic.co/guide/en/beats/winlogbeat/current"
+ heartbeat-ref: "https://www.elastic.co/guide/en/beats/heartbeat/current"
+ journalbeat-ref: "https://www.elastic.co/guide/en/beats/journalbeat/current"
+ ingest-guide: "https://www.elastic.co/guide/en/ingest/current"
+ fleet-guide: "https://www.elastic.co/guide/en/fleet/current"
+ apm-guide-ref: "https://www.elastic.co/guide/en/apm/guide/current"
+ apm-guide-7x: "https://www.elastic.co/guide/en/apm/guide/7.17"
+ apm-app-ref: "https://www.elastic.co/guide/en/kibana/current"
+ apm-agents-ref: "https://www.elastic.co/guide/en/apm/agent"
+ apm-android-ref: "https://www.elastic.co/guide/en/apm/agent/android/current"
+ apm-py-ref: "https://www.elastic.co/guide/en/apm/agent/python/current"
+ apm-py-ref-3x: "https://www.elastic.co/guide/en/apm/agent/python/3.x"
+ apm-node-ref-index: "https://www.elastic.co/guide/en/apm/agent/nodejs"
+ apm-node-ref: "https://www.elastic.co/guide/en/apm/agent/nodejs/current"
+ apm-node-ref-1x: "https://www.elastic.co/guide/en/apm/agent/nodejs/1.x"
+ apm-rum-ref: "https://www.elastic.co/guide/en/apm/agent/rum-js/current"
+ apm-ruby-ref: "https://www.elastic.co/guide/en/apm/agent/ruby/current"
+ apm-java-ref: "https://www.elastic.co/guide/en/apm/agent/java/current"
+ apm-go-ref: "https://www.elastic.co/guide/en/apm/agent/go/current"
+ apm-dotnet-ref: "https://www.elastic.co/guide/en/apm/agent/dotnet/current"
+ apm-php-ref: "https://www.elastic.co/guide/en/apm/agent/php/current"
+ apm-ios-ref: "https://www.elastic.co/guide/en/apm/agent/swift/current"
+ apm-lambda-ref: "https://www.elastic.co/guide/en/apm/lambda/current"
+ apm-attacher-ref: "https://www.elastic.co/guide/en/apm/attacher/current"
+ docker-logging-ref: "https://www.elastic.co/guide/en/beats/loggingplugin/current"
+ esf-ref: "https://www.elastic.co/guide/en/esf/current"
+ kinesis-firehose-ref: "https://www.elastic.co/guide/en/kinesis/{{kinesis_version}}"
+ estc-welcome-current: "https://www.elastic.co/guide/en/starting-with-the-elasticsearch-platform-and-its-solutions/current"
+ estc-welcome: "https://www.elastic.co/guide/en/starting-with-the-elasticsearch-platform-and-its-solutions/current"
+ estc-welcome-all: "https://www.elastic.co/guide/en/starting-with-the-elasticsearch-platform-and-its-solutions"
+ hadoop-ref: "https://www.elastic.co/guide/en/elasticsearch/hadoop/current"
+ stack-ref: "https://www.elastic.co/guide/en/elastic-stack/current"
+ stack-ref-67: "https://www.elastic.co/guide/en/elastic-stack/6.7"
+ stack-ref-68: "https://www.elastic.co/guide/en/elastic-stack/6.8"
+ stack-ref-70: "https://www.elastic.co/guide/en/elastic-stack/7.0"
+ stack-ref-80: "https://www.elastic.co/guide/en/elastic-stack/8.0"
+ stack-ov: "https://www.elastic.co/guide/en/elastic-stack-overview/current"
+ stack-gs: "https://www.elastic.co/guide/en/elastic-stack-get-started/current"
+ stack-gs-current: "https://www.elastic.co/guide/en/elastic-stack-get-started/current"
+ javaclient: "https://www.elastic.co/guide/en/elasticsearch/client/java-api/current"
+ java-api-client: "https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current"
+ java-rest: "https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current"
+ jsclient: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current"
+ jsclient-current: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current"
+ es-ruby-client: "https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current"
+ es-dotnet-client: "https://www.elastic.co/guide/en/elasticsearch/client/net-api/current"
+ es-php-client: "https://www.elastic.co/guide/en/elasticsearch/client/php-api/current"
+ es-python-client: "https://www.elastic.co/guide/en/elasticsearch/client/python-api/current"
+ defguide: "https://www.elastic.co/guide/en/elasticsearch/guide/2.x"
+ painless: "https://www.elastic.co/guide/en/elasticsearch/painless/current"
+ plugins: "https://www.elastic.co/guide/en/elasticsearch/plugins/current"
+ plugins-8x: "https://www.elastic.co/guide/en/elasticsearch/plugins/8.1"
+ plugins-7x: "https://www.elastic.co/guide/en/elasticsearch/plugins/7.17"
+ plugins-6x: "https://www.elastic.co/guide/en/elasticsearch/plugins/6.8"
+ glossary: "https://www.elastic.co/guide/en/elastic-stack-glossary/current"
+ upgrade_guide: "https://www.elastic.co/products/upgrade_guide"
+ blog-ref: "https://www.elastic.co/blog/"
+ curator-ref: "https://www.elastic.co/guide/en/elasticsearch/client/curator/current"
+ curator-ref-current: "https://www.elastic.co/guide/en/elasticsearch/client/curator/current"
+ metrics-ref: "https://www.elastic.co/guide/en/metrics/current"
+ metrics-guide: "https://www.elastic.co/guide/en/metrics/guide/current"
+ logs-ref: "https://www.elastic.co/guide/en/logs/current"
+ logs-guide: "https://www.elastic.co/guide/en/logs/guide/current"
+ uptime-guide: "https://www.elastic.co/guide/en/uptime/current"
+ observability-guide: "https://www.elastic.co/guide/en/observability/current"
+ observability-guide-all: "https://www.elastic.co/guide/en/observability"
+ siem-guide: "https://www.elastic.co/guide/en/siem/guide/current"
+ security-guide: "https://www.elastic.co/guide/en/security/current"
+ security-guide-all: "https://www.elastic.co/guide/en/security"
+ endpoint-guide: "https://www.elastic.co/guide/en/endpoint/current"
+ sql-odbc: "https://www.elastic.co/guide/en/elasticsearch/sql-odbc/current"
+ ecs-ref: "https://www.elastic.co/guide/en/ecs/current"
+ ecs-logging-ref: "https://www.elastic.co/guide/en/ecs-logging/overview/current"
+ ecs-logging-go-logrus-ref: "https://www.elastic.co/guide/en/ecs-logging/go-logrus/current"
+ ecs-logging-go-zap-ref: "https://www.elastic.co/guide/en/ecs-logging/go-zap/current"
+ ecs-logging-go-zerolog-ref: "https://www.elastic.co/guide/en/ecs-logging/go-zap/current"
+ ecs-logging-java-ref: "https://www.elastic.co/guide/en/ecs-logging/java/current"
+ ecs-logging-dotnet-ref: "https://www.elastic.co/guide/en/ecs-logging/dotnet/current"
+ ecs-logging-nodejs-ref: "https://www.elastic.co/guide/en/ecs-logging/nodejs/current"
+ ecs-logging-php-ref: "https://www.elastic.co/guide/en/ecs-logging/php/current"
+ ecs-logging-python-ref: "https://www.elastic.co/guide/en/ecs-logging/python/current"
+ ecs-logging-ruby-ref: "https://www.elastic.co/guide/en/ecs-logging/ruby/current"
+ ml-docs: "https://www.elastic.co/guide/en/machine-learning/current"
+ eland-docs: "https://www.elastic.co/guide/en/elasticsearch/client/eland/current"
+ eql-ref: "https://eql.readthedocs.io/en/latest/query-guide"
+ extendtrial: "https://www.elastic.co/trialextension"
+ wikipedia: "https://en.wikipedia.org/wiki"
+ forum: "https://discuss.elastic.co/"
+ xpack-forum: "https://discuss.elastic.co/c/50-x-pack"
+ security-forum: "https://discuss.elastic.co/c/x-pack/shield"
+ watcher-forum: "https://discuss.elastic.co/c/x-pack/watcher"
+ monitoring-forum: "https://discuss.elastic.co/c/x-pack/marvel"
+ graph-forum: "https://discuss.elastic.co/c/x-pack/graph"
+ apm-forum: "https://discuss.elastic.co/c/apm"
+ enterprise-search-ref: "https://www.elastic.co/guide/en/enterprise-search/current"
+ app-search-ref: "https://www.elastic.co/guide/en/app-search/current"
+ workplace-search-ref: "https://www.elastic.co/guide/en/workplace-search/current"
+ enterprise-search-node-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/enterprise-search-node/current"
+ enterprise-search-php-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/php/current"
+ enterprise-search-python-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/python/current"
+ enterprise-search-ruby-ref: "https://www.elastic.co/guide/en/enterprise-search-clients/ruby/current"
+ elastic-maps-service: "https://maps.elastic.co"
+ integrations-docs: "https://docs.elastic.co/en/integrations"
+ integrations-devguide: "https://www.elastic.co/guide/en/integrations-developer/current"
+ time-units: "https://www.elastic.co/guide/en/elasticsearch/reference/current/api-conventions.html#time-units"
+ byte-units: "https://www.elastic.co/guide/en/elasticsearch/reference/current/api-conventions.html#byte-units"
+ apm-py-ref-v: "https://www.elastic.co/guide/en/apm/agent/python/current"
+ apm-node-ref-v: "https://www.elastic.co/guide/en/apm/agent/nodejs/current"
+ apm-rum-ref-v: "https://www.elastic.co/guide/en/apm/agent/rum-js/current"
+ apm-ruby-ref-v: "https://www.elastic.co/guide/en/apm/agent/ruby/current"
+ apm-java-ref-v: "https://www.elastic.co/guide/en/apm/agent/java/current"
+ apm-go-ref-v: "https://www.elastic.co/guide/en/apm/agent/go/current"
+ apm-ios-ref-v: "https://www.elastic.co/guide/en/apm/agent/swift/current"
+ apm-dotnet-ref-v: "https://www.elastic.co/guide/en/apm/agent/dotnet/current"
+ apm-php-ref-v: "https://www.elastic.co/guide/en/apm/agent/php/current"
+ ecloud: "Elastic Cloud"
+ esf: "Elastic Serverless Forwarder"
+ ess: "Elasticsearch Service"
+ ece: "Elastic Cloud Enterprise"
+ eck: "Elastic Cloud on Kubernetes"
+ serverless-full: "Elastic Cloud Serverless"
+ serverless-short: "Serverless"
+ es-serverless: "Elasticsearch Serverless"
+ es3: "Elasticsearch Serverless"
+ obs-serverless: "Elastic Observability Serverless"
+ sec-serverless: "Elastic Security Serverless"
+ serverless-docs: "https://docs.elastic.co/serverless"
+ cloud: "https://www.elastic.co/guide/en/cloud/current"
+ ess-utm-params: "?page=docs&placement=docs-body"
+ ess-baymax: "?page=docs&placement=docs-body"
+ ess-trial: "https://cloud.elastic.co/registration?page=docs&placement=docs-body"
+ ess-product: "https://www.elastic.co/cloud/elasticsearch-service?page=docs&placement=docs-body"
+ ess-console: "https://cloud.elastic.co?page=docs&placement=docs-body"
+ ess-console-name: "Elasticsearch Service Console"
+ ess-deployments: "https://cloud.elastic.co/deployments?page=docs&placement=docs-body"
+ ece-ref: "https://www.elastic.co/guide/en/cloud-enterprise/current"
+ eck-ref: "https://www.elastic.co/guide/en/cloud-on-k8s/current"
+ ess-leadin: "You can run Elasticsearch on your own hardware or use our hosted Elasticsearch Service that is available on AWS, GCP, and Azure. https://cloud.elastic.co/registration{ess-utm-params}[Try the Elasticsearch Service for free]."
+ ess-leadin-short: "Our hosted Elasticsearch Service is available on AWS, GCP, and Azure, and you can https://cloud.elastic.co/registration{ess-utm-params}[try it for free]."
+ ess-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/logo_cloud.svg[link=\"https://cloud.elastic.co/registration{ess-utm-params}\", title=\"Supported on Elasticsearch Service\"]"
+ ece-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/logo_cloud_ece.svg[link=\"https://cloud.elastic.co/registration{ess-utm-params}\", title=\"Supported on Elastic Cloud Enterprise\"]"
+ cloud-only: "This feature is designed for indirect use by https://cloud.elastic.co/registration{ess-utm-params}[Elasticsearch Service], https://www.elastic.co/guide/en/cloud-enterprise/{ece-version-link}[Elastic Cloud Enterprise], and https://www.elastic.co/guide/en/cloud-on-k8s/current[Elastic Cloud on Kubernetes]. Direct use is not supported."
+ ess-setting-change: "image:https://doc-icons.s3.us-east-2.amazonaws.com/logo_cloud.svg[link=\"{ess-trial}\", title=\"Supported on {ess}\"] indicates a change to a supported https://www.elastic.co/guide/en/cloud/current/ec-add-user-settings.html[user setting] for Elasticsearch Service."
+ ess-skip-section: "If you use Elasticsearch Service, skip this section. Elasticsearch Service handles these changes for you."
+ api-cloud: "https://www.elastic.co/docs/api/doc/cloud"
+ api-ece: "https://www.elastic.co/docs/api/doc/cloud-enterprise"
+ api-kibana-serverless: "https://www.elastic.co/docs/api/doc/serverless"
+ es-feature-flag: "This feature is in development and not yet available for use. This documentation is provided for informational purposes only."
+ es-ref-dir: "'{{elasticsearch-root}}/docs/reference'"
+ apm-app: "APM app"
+ uptime-app: "Uptime app"
+ synthetics-app: "Synthetics app"
+ logs-app: "Logs app"
+ metrics-app: "Metrics app"
+ infrastructure-app: "Infrastructure app"
+ siem-app: "SIEM app"
+ security-app: "Elastic Security app"
+ ml-app: "Machine Learning"
+ dev-tools-app: "Dev Tools"
+ ingest-manager-app: "Ingest Manager"
+ stack-manage-app: "Stack Management"
+ stack-monitor-app: "Stack Monitoring"
+ alerts-ui: "Alerts and Actions"
+ rules-ui: "Rules"
+ rac-ui: "Rules and Connectors"
+ connectors-ui: "Connectors"
+ connectors-feature: "Actions and Connectors"
+ stack-rules-feature: "Stack Rules"
+ user-experience: "User Experience"
+ ems: "Elastic Maps Service"
+ ems-init: "EMS"
+ hosted-ems: "Elastic Maps Server"
+ ipm-app: "Index Pattern Management"
+ ingest-pipelines: "ingest pipelines"
+ ingest-pipelines-app: "Ingest Pipelines"
+ ingest-pipelines-cap: "Ingest pipelines"
+ ls-pipelines: "Logstash pipelines"
+ ls-pipelines-app: "Logstash Pipelines"
+ maint-windows: "maintenance windows"
+ maint-windows-app: "Maintenance Windows"
+ maint-windows-cap: "Maintenance windows"
+ custom-roles-app: "Custom Roles"
+ data-source: "data view"
+ data-sources: "data views"
+ data-source-caps: "Data View"
+ data-sources-caps: "Data Views"
+ data-source-cap: "Data view"
+ data-sources-cap: "Data views"
+ project-settings: "Project settings"
+ manage-app: "Management"
+ index-manage-app: "Index Management"
+ data-views-app: "Data Views"
+ rules-app: "Rules"
+ saved-objects-app: "Saved Objects"
+ tags-app: "Tags"
+ api-keys-app: "API keys"
+ transforms-app: "Transforms"
+ connectors-app: "Connectors"
+ files-app: "Files"
+ reports-app: "Reports"
+ maps-app: "Maps"
+ alerts-app: "Alerts"
+ crawler: "Enterprise Search web crawler"
+ ents: "Enterprise Search"
+ app-search-crawler: "App Search web crawler"
+ agent: "Elastic Agent"
+ agents: "Elastic Agents"
+ fleet: "Fleet"
+ fleet-server: "Fleet Server"
+ integrations-server: "Integrations Server"
+ ingest-manager: "Ingest Manager"
+ ingest-management: "ingest management"
+ package-manager: "Elastic Package Manager"
+ integrations: "Integrations"
+ package-registry: "Elastic Package Registry"
+ artifact-registry: "Elastic Artifact Registry"
+ aws: "AWS"
+ stack: "Elastic Stack"
+ xpack: "X-Pack"
+ es: "Elasticsearch"
+ kib: "Kibana"
+ esms: "Elastic Stack Monitoring Service"
+ esms-init: "ESMS"
+ ls: "Logstash"
+ beats: "Beats"
+ auditbeat: "Auditbeat"
+ filebeat: "Filebeat"
+ heartbeat: "Heartbeat"
+ metricbeat: "Metricbeat"
+ packetbeat: "Packetbeat"
+ winlogbeat: "Winlogbeat"
+ functionbeat: "Functionbeat"
+ journalbeat: "Journalbeat"
+ es-sql: "Elasticsearch SQL"
+ esql: "ES|QL"
+ elastic-agent: "Elastic Agent"
+ k8s: "Kubernetes"
+ log-driver-long: "Elastic Logging Plugin for Docker"
+ security: "X-Pack security"
+ security-features: "security features"
+ operator-feature: "operator privileges feature"
+ es-security-features: "Elasticsearch security features"
+ stack-security-features: "Elastic Stack security features"
+ endpoint-sec: "Endpoint Security"
+ endpoint-cloud-sec: "Endpoint and Cloud Security"
+ elastic-defend: "Elastic Defend"
+ elastic-sec: "Elastic Security"
+ elastic-endpoint: "Elastic Endpoint"
+ swimlane: "Swimlane"
+ sn: "ServiceNow"
+ sn-itsm: "ServiceNow ITSM"
+ sn-itom: "ServiceNow ITOM"
+ sn-sir: "ServiceNow SecOps"
+ jira: "Jira"
+ ibm-r: "IBM Resilient"
+ webhook: "Webhook"
+ webhook-cm: "Webhook - Case Management"
+ opsgenie: "Opsgenie"
+ bedrock: "Amazon Bedrock"
+ gemini: "Google Gemini"
+ hive: "TheHive"
+ monitoring: "X-Pack monitoring"
+ monitor-features: "monitoring features"
+ stack-monitor-features: "Elastic Stack monitoring features"
+ watcher: "Watcher"
+ alert-features: "alerting features"
+ reporting: "X-Pack reporting"
+ report-features: "reporting features"
+ graph: "X-Pack graph"
+ graph-features: "graph analytics features"
+ searchprofiler: "Search Profiler"
+ xpackml: "X-Pack machine learning"
+ ml: "machine learning"
+ ml-cap: "Machine learning"
+ ml-init: "ML"
+ ml-features: "machine learning features"
+ stack-ml-features: "Elastic Stack machine learning features"
+ ccr: "cross-cluster replication"
+ ccr-cap: "Cross-cluster replication"
+ ccr-init: "CCR"
+ ccs: "cross-cluster search"
+ ccs-cap: "Cross-cluster search"
+ ccs-init: "CCS"
+ ilm: "index lifecycle management"
+ ilm-cap: "Index lifecycle management"
+ ilm-init: "ILM"
+ dlm: "data lifecycle management"
+ dlm-cap: "Data lifecycle management"
+ dlm-init: "DLM"
+ search-snap: "searchable snapshot"
+ search-snaps: "searchable snapshots"
+ search-snaps-cap: "Searchable snapshots"
+ slm: "snapshot lifecycle management"
+ slm-cap: "Snapshot lifecycle management"
+ slm-init: "SLM"
+ rollup-features: "data rollup features"
+ ipm: "index pattern management"
+ ipm-cap: "Index pattern"
+ rollup: "rollup"
+ rollup-cap: "Rollup"
+ rollups: "rollups"
+ rollups-cap: "Rollups"
+ rollup-job: "rollup job"
+ rollup-jobs: "rollup jobs"
+ rollup-jobs-cap: "Rollup jobs"
+ dfeed: "datafeed"
+ dfeeds: "datafeeds"
+ dfeed-cap: "Datafeed"
+ dfeeds-cap: "Datafeeds"
+ ml-jobs: "machine learning jobs"
+ ml-jobs-cap: "Machine learning jobs"
+ anomaly-detect: "anomaly detection"
+ anomaly-detect-cap: "Anomaly detection"
+ anomaly-job: "anomaly detection job"
+ anomaly-jobs: "anomaly detection jobs"
+ anomaly-jobs-cap: "Anomaly detection jobs"
+ dataframe: "data frame"
+ dataframes: "data frames"
+ dataframe-cap: "Data frame"
+ dataframes-cap: "Data frames"
+ watcher-transform: "payload transform"
+ watcher-transforms: "payload transforms"
+ watcher-transform-cap: "Payload transform"
+ watcher-transforms-cap: "Payload transforms"
+ transform: "transform"
+ transforms: "transforms"
+ transform-cap: "Transform"
+ transforms-cap: "Transforms"
+ dataframe-transform: "transform"
+ dataframe-transform-cap: "Transform"
+ dataframe-transforms: "transforms"
+ dataframe-transforms-cap: "Transforms"
+ dfanalytics-cap: "Data frame analytics"
+ dfanalytics: "data frame analytics"
+ dataframe-analytics-config: "'{dataframe} analytics config'"
+ dfanalytics-job: "'{dataframe} analytics job'"
+ dfanalytics-jobs: "'{dataframe} analytics jobs'"
+ dfanalytics-jobs-cap: "'{dataframe-cap} analytics jobs'"
+ cdataframe: "continuous data frame"
+ cdataframes: "continuous data frames"
+ cdataframe-cap: "Continuous data frame"
+ cdataframes-cap: "Continuous data frames"
+ cdataframe-transform: "continuous transform"
+ cdataframe-transforms: "continuous transforms"
+ cdataframe-transforms-cap: "Continuous transforms"
+ ctransform: "continuous transform"
+ ctransform-cap: "Continuous transform"
+ ctransforms: "continuous transforms"
+ ctransforms-cap: "Continuous transforms"
+ oldetection: "outlier detection"
+ oldetection-cap: "Outlier detection"
+ olscore: "outlier score"
+ olscores: "outlier scores"
+ fiscore: "feature influence score"
+ evaluatedf-api: "evaluate {dataframe} analytics API"
+ evaluatedf-api-cap: "Evaluate {dataframe} analytics API"
+ binarysc: "binary soft classification"
+ binarysc-cap: "Binary soft classification"
+ regression: "regression"
+ regression-cap: "Regression"
+ reganalysis: "regression analysis"
+ reganalysis-cap: "Regression analysis"
+ depvar: "dependent variable"
+ feature-var: "feature variable"
+ feature-vars: "feature variables"
+ feature-vars-cap: "Feature variables"
+ classification: "classification"
+ classification-cap: "Classification"
+ classanalysis: "classification analysis"
+ classanalysis-cap: "Classification analysis"
+ infer-cap: "Inference"
+ infer: "inference"
+ lang-ident-cap: "Language identification"
+ lang-ident: "language identification"
+ data-viz: "Data Visualizer"
+ file-data-viz: "File Data Visualizer"
+ feat-imp: "feature importance"
+ feat-imp-cap: "Feature importance"
+ nlp: "natural language processing"
+ nlp-cap: "Natural language processing"
+ apm-agent: "APM agent"
+ apm-go-agent: "Elastic APM Go agent"
+ apm-go-agents: "Elastic APM Go agents"
+ apm-ios-agent: "Elastic APM iOS agent"
+ apm-ios-agents: "Elastic APM iOS agents"
+ apm-java-agent: "Elastic APM Java agent"
+ apm-java-agents: "Elastic APM Java agents"
+ apm-dotnet-agent: "Elastic APM .NET agent"
+ apm-dotnet-agents: "Elastic APM .NET agents"
+ apm-node-agent: "Elastic APM Node.js agent"
+ apm-node-agents: "Elastic APM Node.js agents"
+ apm-php-agent: "Elastic APM PHP agent"
+ apm-php-agents: "Elastic APM PHP agents"
+ apm-py-agent: "Elastic APM Python agent"
+ apm-py-agents: "Elastic APM Python agents"
+ apm-ruby-agent: "Elastic APM Ruby agent"
+ apm-ruby-agents: "Elastic APM Ruby agents"
+ apm-rum-agent: "Elastic APM Real User Monitoring (RUM) JavaScript agent"
+ apm-rum-agents: "Elastic APM RUM JavaScript agents"
+ apm-lambda-ext: "Elastic APM AWS Lambda extension"
+ project-monitors: "project monitors"
+ project-monitors-cap: "Project monitors"
+ private-location: "Private Location"
+ private-locations: "Private Locations"
+ pwd: "YOUR_PASSWORD"
+ esh: "ES-Hadoop"
+ default-dist: "default distribution"
+ oss-dist: "OSS-only distribution"
+ observability: "Observability"
+ api-request-title: "Request"
+ api-prereq-title: "Prerequisites"
+ api-description-title: "Description"
+ api-path-parms-title: "Path parameters"
+ api-query-parms-title: "Query parameters"
+ api-request-body-title: "Request body"
+ api-response-codes-title: "Response codes"
+ api-response-body-title: "Response body"
+ api-example-title: "Example"
+ api-examples-title: "Examples"
+ api-definitions-title: "Properties"
+ multi-arg: "†footnoteref:[multi-arg,This parameter accepts multiple arguments.]"
+ multi-arg-ref: "†footnoteref:[multi-arg]"
+ yes-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/icon-yes.png[Yes,20,15]"
+ no-icon: "image:https://doc-icons.s3.us-east-2.amazonaws.com/icon-no.png[No,20,15]"
+ es-repo: "https://github.com/elastic/elasticsearch/"
+ es-issue: "https://github.com/elastic/elasticsearch/issues/"
+ es-pull: "https://github.com/elastic/elasticsearch/pull/"
+ es-commit: "https://github.com/elastic/elasticsearch/commit/"
+ kib-repo: "https://github.com/elastic/kibana/"
+ kib-issue: "https://github.com/elastic/kibana/issues/"
+ kibana-issue: "'{kib-repo}issues/'"
+ kib-pull: "https://github.com/elastic/kibana/pull/"
+ kibana-pull: "'{kib-repo}pull/'"
+ kib-commit: "https://github.com/elastic/kibana/commit/"
+ ml-repo: "https://github.com/elastic/ml-cpp/"
+ ml-issue: "https://github.com/elastic/ml-cpp/issues/"
+ ml-pull: "https://github.com/elastic/ml-cpp/pull/"
+ ml-commit: "https://github.com/elastic/ml-cpp/commit/"
+ apm-repo: "https://github.com/elastic/apm-server/"
+ apm-issue: "https://github.com/elastic/apm-server/issues/"
+ apm-pull: "https://github.com/elastic/apm-server/pull/"
+ kibana-blob: "https://github.com/elastic/kibana/blob/current/"
+ apm-get-started-ref: "https://www.elastic.co/guide/en/apm/get-started/current"
+ apm-server-ref: "https://www.elastic.co/guide/en/apm/server/current"
+ apm-server-ref-v: "https://www.elastic.co/guide/en/apm/server/current"
+ apm-server-ref-m: "https://www.elastic.co/guide/en/apm/server/master"
+ apm-server-ref-62: "https://www.elastic.co/guide/en/apm/server/6.2"
+ apm-server-ref-64: "https://www.elastic.co/guide/en/apm/server/6.4"
+ apm-server-ref-70: "https://www.elastic.co/guide/en/apm/server/7.0"
+ apm-overview-ref-v: "https://www.elastic.co/guide/en/apm/get-started/current"
+ apm-overview-ref-70: "https://www.elastic.co/guide/en/apm/get-started/7.0"
+ apm-overview-ref-m: "https://www.elastic.co/guide/en/apm/get-started/master"
+ infra-guide: "https://www.elastic.co/guide/en/infrastructure/guide/current"
+ a-data-source: "a data view"
+ icon-bug: "pass:[]"
+ icon-checkInCircleFilled: "pass:[]"
+ icon-warningFilled: "pass:[]"
diff --git a/docs/index.asciidoc b/docs/index.asciidoc
deleted file mode 100644
index 3b966df..0000000
--- a/docs/index.asciidoc
+++ /dev/null
@@ -1,18 +0,0 @@
-:ecs-repo-dir: {ecs-logging-root}/docs/
-
-include::{docs-root}/shared/versions/stack/current.asciidoc[]
-include::{docs-root}/shared/attributes.asciidoc[]
-
-ifdef::env-github[]
-NOTE: For the best reading experience,
-please view this documentation at https://www.elastic.co/guide/en/ecs-logging/nodejs/current/index.html[elastic.co]
-endif::[]
-
-= ECS Logging Node.js Reference
-
-ifndef::env-github[]
-include::./intro.asciidoc[Introduction]
-include::./pino.asciidoc[Logging with Pino]
-include::./winston.asciidoc[Logging with Winston]
-include::./morgan.asciidoc[Logging with Morgan]
-endif::[]
diff --git a/docs/intro.asciidoc b/docs/intro.asciidoc
deleted file mode 100644
index 20fce91..0000000
--- a/docs/intro.asciidoc
+++ /dev/null
@@ -1,33 +0,0 @@
-[[intro]]
-== Introduction
-
-Node.js ECS loggers are formatter plugins for your favorite logging libraries.
-They make it easy to format your logs into ECS-compatible JSON. In combination
-with https://www.elastic.co/products/beats/filebeat[filebeat] you can send your
-logs directly to Elasticsearch and leverage {observability-guide}/monitor-logs.html[Kibana's Logs app]
-to inspect all logs in one single place.
-
-The Node.js ECS logging formatters log structured JSON and support serialization
-of Error objects and HTTP Request and Response objects from Node.js core and
-popular web frameworks. A minimal log record includes the following fields:
-
-[source,json]
-----
-{
- "@timestamp": "2021-01-13T21:32:38.095Z",
- "log.level": "info",
- "message": "hi",
- "ecs.version": "8.10.0"
-}
-----
-
-TIP: Want to learn more about ECS, ECS logging, and other available language plugins?
-See the {ecs-logging-ref}/intro.html[ECS logging guide].
-
-Ready to jump into Node.js ECS logging?
-
-- <>
-- <>
-- <>
-
-If you'd like to try out a tutorial using Node.js ECS logging with winston, see {cloud}/ec-getting-started-search-use-cases-node-logs.html[Ingest logs from a Node.js web application using Filebeat].
diff --git a/docs/morgan.asciidoc b/docs/morgan.asciidoc
deleted file mode 100644
index 1166da5..0000000
--- a/docs/morgan.asciidoc
+++ /dev/null
@@ -1,226 +0,0 @@
-[[morgan]]
-== ECS Logging with Morgan
-
-This Node.js package provides a formatter for the https://github.com/expressjs/morgan#readme[morgan]
-logging middleware -- commonly used with Express -- compatible with
-{ecs-logging-ref}/intro.html[Elastic Common Schema (ECS) logging].
-In combination with the https://www.elastic.co/beats/filebeat[Filebeat] shipper,
-you can https://www.elastic.co/log-monitoring[monitor all your logs] in one
-place in the Elastic Stack.
-
-
-[float]
-=== Setup
-
-[float]
-[[morgan-setup-step-1]]
-==== Step 1: Install
-
-[source,cmd]
-----
-$ npm install @elastic/ecs-morgan-format
-----
-
-
-[float]
-[[morgan-setup-step-2]]
-==== Step 2: Configure
-
-[source,js]
-----
-const app = require('express')();
-const morgan = require('morgan');
-const { ecsFormat } = require('@elastic/ecs-morgan-format');
-
-app.use(morgan(ecsFormat(/* options */))); <1>
-
-// ...
-app.get('/', function (req, res) {
- res.send('hello, world!');
-})
-app.listen(3000);
-----
-<1> Pass the ECS formatter to `morgan()`.
-
-
-[float]
-[[morgan-setup-step-3]]
-==== Step 3: Configure Filebeat
-
-The best way to collect the logs once they are ECS-formatted is with {filebeat-ref}[Filebeat]:
-
-include::{ecs-repo-dir}/setup.asciidoc[tag=configure-filebeat]
-
-
-[float]
-[[morgan-usage]]
-=== Usage
-
-[source,js]
-----
-const app = require('express')();
-const morgan = require('morgan');
-const { ecsFormat } = require('@elastic/ecs-morgan-format');
-
-app.use(morgan(ecsFormat(/* options */))); <1>
-
-app.get('/', function (req, res) {
- res.send('hello, world!');
-})
-app.get('/error', function (req, res, next) {
- next(new Error('boom'));
-})
-
-app.listen(3000)
-----
-<1> See available options <>.
-
-Running this script (the full example is https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-morgan-format/examples/express.js[here])
-and making a request (via `curl -i localhost:3000/`) will produce log output
-similar to the following:
-
-[source,cmd]
-----
-% node examples/express.js | jq . # piping to jq for pretty-printing
-{
- "@timestamp": "2021-01-16T00:03:23.279Z",
- "log.level": "info",
- "message": "::1 - - [16/Jan/2021:00:03:23 +0000] \"GET / HTTP/1.1\" 200 13 \"-\" \"curl/7.64.1\"",
- "ecs.version": "8.10.0",
- "http": {
- "version": "1.1",
- "request": {
- "method": "GET",
- "headers": {
- "host": "localhost:3000",
- "accept": "*/*"
- }
- },
- "response": {
- "status_code": 200,
- "headers": {
- "x-powered-by": "Express",
- "content-type": "text/html; charset=utf-8",
- "etag": "W/\"d-HwnTDHB9U/PRbFMN1z1wps51lqk\""
- },
- "body": {
- "bytes": 13
- }
- }
- },
- "url": {
- "path": "/",
- "domain": "localhost",
- "full": "http://localhost:3000/"
- },
- "user_agent": {
- "original": "curl/7.64.1"
- }
-}
-----
-
-[float]
-[[morgan-format-options]]
-=== Format Options
-
-You can pass any https://github.com/expressjs/morgan#morganformat-options[`format` argument]
-you would normally pass to `morgan()`, and the log "message" field will use the
-specified format. The default is https://github.com/expressjs/morgan#combined[`combined`].
-
-[source,js]
-----
-const app = require('express')();
-const morgan = require('morgan');
-const { ecsFormat } = require('@elastic/ecs-morgan-format');
-
-app.use(morgan(ecsFormat({ format: 'tiny' }))); <1>
-// ...
-----
-<1> If "format" is the only option you are using, you may pass it as `ecsFormat('tiny')`.
-
-[float]
-[[morgan-log-level]]
-=== log.level
-
-The `log.level` field will be "error" for response codes >= 500, otherwise
-"info". For example, running https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-morgan-format/examples/express.js[examples/express.js]
-again, a `curl -i localhost:3000/error` will yield:
-
-[source,cmd]
-----
-% node examples/express.js | jq .
-{
- "@timestamp": "2021-01-18T17:52:12.810Z",
- "log.level": "error",
- "message": "::1 - - [18/Jan/2021:17:52:12 +0000] \"GET /error HTTP/1.1\" 500 1416 \"-\" \"curl/7.64.1\"",
- "http": {
- "response": {
- "status_code": 500,
- ...
-----
-
-
-[float]
-[[morgan-apm]]
-=== Log Correlation with APM
-
-This ECS log formatter integrates with https://www.elastic.co/apm[Elastic APM].
-If your Node app is using the {apm-node-ref}/intro.html[Node.js Elastic APM Agent],
-then a number of fields are added to log records to correlate between APM
-services or traces and logging data:
-
-- Log statements (e.g. `logger.info(...)`) called when there is a current
- tracing span will include {ecs-ref}/ecs-tracing.html[tracing fields] --
- `trace.id`, `transaction.id`.
-- A number of service identifier fields determined by or configured on the APM
- agent allow cross-linking between services and logs in Kibana --
- `service.name`, `service.version`, `service.environment`, `service.node.name`.
-- `event.dataset` enables {observability-guide}/inspect-log-anomalies.html[log
- rate anomaly detection] in the Elastic Observability app.
-
-For example, running https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-morgan-format/examples/express-with-apm.js[examples/express-with-apm.js] and `curl -i localhost:3000/` results in a log record with the following:
-
-[source,cmd]
-----
-% node examples/express-with-apm.js | jq .
-{
- // The same fields as before, plus:
- "service.name": "express-with-elastic-apm",
- "service.version": "1.1.0",
- "service.environment": "development",
- "event.dataset": "express-with-elastic-apm",
- "trace.id": "116d46f667a7600deed9c41fa015f7de",
- "transaction.id": "b84fb72d7bf42866"
-}
-----
-
-These IDs match trace data reported by the APM agent.
-
-Integration with Elastic APM can be explicitly disabled via the
-`apmIntegration: false` option, for example:
-
-[source,js]
-----
-app.use(morgan(ecsFormat({ apmIntegration: false })));
-----
-
-
-[float]
-[[morgan-ref]]
-=== Reference
-
-[float]
-[[morgan-ref-ecsFormat]]
-==== `ecsFormat([options])`
-
-* `options` +{type-object}+ The following options are supported:
-** `format` +{type-string}+ A format *name* (e.g. 'combined'), format function (e.g. `morgan.combined`), or a format string (e.g. ':method :url :status'). This is used to format the "message" field. Defaults to `morgan.combined`.
-** `convertErr` +{type-boolean}+ Whether to convert a logged `err` field to ECS error fields. *Default:* `true`.
-** `apmIntegration` +{type-boolean}+ Whether to enable APM agent integration. *Default:* `true`.
-** `serviceName` +{type-string}+ A "service.name" value. If specified this overrides any value from an active APM agent.
-** `serviceVersion` +{type-string}+ A "service.version" value. If specified this overrides any value from an active APM agent.
-** `serviceEnvironment` +{type-string}+ A "service.environment" value. If specified this overrides any value from an active APM agent.
-** `serviceNodeName` +{type-string}+ A "service.node.name" value. If specified this overrides any value from an active APM agent.
-** `eventDataset` +{type-string}+ A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
-
-Create a formatter for morgan that emits in ECS Logging format.
diff --git a/docs/pino.asciidoc b/docs/pino.asciidoc
deleted file mode 100644
index f0855d5..0000000
--- a/docs/pino.asciidoc
+++ /dev/null
@@ -1,286 +0,0 @@
-[[pino]]
-== ECS Logging with Pino
-
-This Node.js package provides a formatter for the https://getpino.io[pino]
-logger, compatible with {ecs-logging-ref}/intro.html[Elastic Common Schema (ECS) logging].
-In combination with the https://www.elastic.co/beats/filebeat[Filebeat] shipper,
-you can https://www.elastic.co/log-monitoring[monitor all your logs] in one
-place in the Elastic Stack. `pino` 6.x, 7.x, and 8.x versions are supported.
-
-
-[float]
-=== Setup
-
-[float]
-[[pino-setup-step-1]]
-==== Step 1: Install
-
-[source,cmd]
-----
-$ npm install @elastic/ecs-pino-format
-----
-
-[float]
-[[pino-setup-step-2]]
-==== Step 2: Configure
-
-[source,js]
-----
-const { ecsFormat } = require('@elastic/ecs-pino-format');
-const pino = require('pino');
-
-const log = pino(ecsFormat(/* options */)); <1>
-log.info('hi');
-log.error({ err: new Error('boom') }, 'oops there is a problem');
-// ...
-----
-<1> This will https://getpino.io/#/docs/api?id=options[configure] Pino's `formatters`, `messageKey` and `timestamp` options.
-
-See usage discussion and examples below.
-
-[float]
-[[pino-setup-step-3]]
-==== Step 3: Configure Filebeat
-
-The best way to collect the logs once they are ECS-formatted is with {filebeat-ref}[Filebeat]:
-
-include::{ecs-repo-dir}/setup.asciidoc[tag=configure-filebeat]
-
-
-[float]
-[[pino-usage]]
-=== Usage
-
-[source,js]
-----
-const { ecsFormat } = require('@elastic/ecs-pino-format');
-const pino = require('pino');
-
-const log = pino(ecsFormat(/* options */)); <1>
-log.info('Hello world');
-
-const child = log.child({ module: 'foo' });
-child.warn('From child');
-----
-<1> See available options <>.
-
-Running this will produce log output similar to the following:
-
-[source,cmd]
-----
-{"log.level":"info","@timestamp":"2023-10-14T02:07:47.901Z","process.pid":56645,"host.hostname":"pink.local","ecs.version":"8.10.0","message":"Hello world"}
-{"log.level":"warn","@timestamp":"2023-10-14T02:07:47.901Z","process.pid":56645,"host.hostname":"pink.local","ecs.version":"8.10.0","module":"foo","message":"From child"}
-----
-
-[float]
-[[pino-error-logging]]
-=== Error Logging
-
-By default, the formatter will convert an `err` field that is an Error instance
-to {ecs-ref}/ecs-error.html[ECS Error fields].
-For example:
-
-[source,js]
-----
-const { ecsFormat } = require('@elastic/ecs-pino-format');
-const pino = require('pino');
-const log = pino(ecsFormat());
-
-const myErr = new Error('boom');
-log.info({ err: myErr }, 'oops');
-----
-
-will yield (pretty-printed for readability):
-
-[source,cmd]
-----
-% node examples/error.js | jq .
-{
- "log.level": "info",
- "@timestamp": "2021-01-26T17:02:23.697Z",
- ...
- "error": {
- "type": "Error",
- "message": "boom",
- "stack_trace": "Error: boom\n at Object. (..."
- },
- "message": "oops"
-}
-----
-
-This is analogous to and overrides
-https://getpino.io/#/docs/api?id=serializers-object[Pino's default err serializer].
-Special handling of the `err` field can be disabled via the `convertErr: false` option:
-
-[source,js]
-----
-const log = pino(ecsFormat({ convertErr: false }));
-----
-
-
-[float]
-[[pino-http-logging]]
-=== HTTP Request and Response Logging
-
-With the `convertReqRes: true` option, the formatter will automatically
-convert Node.js core https://nodejs.org/api/http.html#http_class_http_incomingmessage[request]
-and https://nodejs.org/api/http.html#http_class_http_serverresponse[response]
-objects when passed as the `req` and `res` fields, respectively.
-(This option replaces the usage of `req` and `res` https://getpino.io/#/docs/api?id=pinostdserializers-object[Pino serializers].)
-
-[source,js]
-----
-const http = require('http');
-const { ecsFormat } = require('@elastic/ecs-pino-format');
-const pino = require('pino');
-
-const log = pino(ecsFormat({ convertReqRes: true })); <1>
-
-const server = http.createServer(function handler (req, res) {
- res.setHeader('Foo', 'Bar');
- res.end('ok');
- log.info({ req, res }, 'handled request'); <2>
-});
-
-server.listen(3000, () => {
- log.info('listening at http://localhost:3000');
-}
-----
-<1> use `convertReqRes` option
-<2> log with `req` and/or `res` fields
-
-This will produce logs with request and response info using
-{ecs-ref}/ecs-http.html[ECS HTTP fields].
-For example:
-
-[source,cmd]
-----
-% node examples/http.js | jq . # using jq for pretty printing
-... # run 'curl http://localhost:3000/'
-{
- "log.level": "info",
- "@timestamp": "2023-10-14T02:10:14.477Z",
- "process.pid": 56697,
- "host.hostname": "pink.local",
- "ecs.version": "8.10.0",
- "http": {
- "version": "1.1",
- "request": {
- "method": "GET",
- "headers": {
- "host": "localhost:3000",
- "user-agent": "curl/8.1.2",
- "accept": "*/*"
- }
- },
- "response": {
- "status_code": 200,
- "headers": {
- "foo": "Bar"
- }
- }
- },
- "url": {
- "full": "http://localhost:3000/",
- "path": "/"
- },
- "client": {
- "address": "::ffff:127.0.0.1",
- "ip": "::ffff:127.0.0.1",
- "port": 49504
- },
- "user_agent": {
- "original": "curl/8.1.2"
- },
- "message": "handled request"
-}
-----
-
-The https://github.com/elastic/ecs-logging-nodejs/tree/main/packages/ecs-pino-format/examples[examples/ directory]
-shows sample programs using request and response logging:
-https://github.com/elastic/ecs-logging-nodejs/tree/main/packages/ecs-pino-format/examples/express-simple.js[with Express],
-https://github.com/elastic/ecs-logging-nodejs/tree/main/packages/ecs-pino-format/examples/express-with-pino-http.js[with the pino-http middleware package],
-etc.
-
-
-[float]
-[[pino-apm]]
-=== Log Correlation with APM
-
-This ECS log formatter integrates with https://www.elastic.co/apm[Elastic APM].
-If your Node app is using the {apm-node-ref}/intro.html[Node.js Elastic APM Agent],
-then a number of fields are added to log records to correlate between APM
-services or traces and logging data:
-
-- Log statements (e.g. `logger.info(...)`) called when there is a current
- tracing span will include {ecs-ref}/ecs-tracing.html[tracing fields] --
- `trace.id`, `transaction.id`, `span.id`.
-- A number of service identifier fields determined by or configured on the APM
- agent allow cross-linking between services and logs in Kibana --
- `service.name`, `service.version`, `service.environment`, `service.node.name`.
-- `event.dataset` enables {observability-guide}/inspect-log-anomalies.html[log
- rate anomaly detection] in the Elastic Observability app.
-
-For example, running https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-pino-format/examples/http-with-elastic-apm.js[examples/http-with-elastic-apm.js] and `curl -i localhost:3000/` results in a log record with the following:
-
-[source,cmd]
-----
-% node examples/http-with-elastic-apm.js | jq .
-...
- "service.name": "http-with-elastic-apm",
- "service.version": "1.4.0",
- "service.environment": "development",
- "event.dataset": "http-with-elastic-apm",
- "trace.id": "9f338eae7211b7993b98929046aed21d",
- "transaction.id": "2afbef5642cc7a3f",
-...
-----
-
-These IDs match trace data reported by the APM agent.
-
-Integration with Elastic APM can be explicitly disabled via the
-`apmIntegration: false` option, for example:
-
-[source,js]
-----
-const log = pino(ecsFormat({ apmIntegration: false }));
-----
-
-
-[float]
-[[pino-considerations]]
-=== Limitations and Considerations
-
-The https://github.com/elastic/ecs-logging/tree/main/spec[ecs-logging spec]
-suggests that the first three fields in log records must be `@timestamp`,
-`log.level`, and `message`. Pino does not provide a mechanism to put
-the `message` field near the front. Given that ordering of ecs-logging fields
-is for *human readability* and does not affect interoperability, this is not
-considered a significant concern.
-
-The hooks that Pino currently provides do not enable this package to convert
-fields passed to `.child({ ... })`. This means that, even with the
-`convertReqRes` option, a call to `.child({ req })` will *not* convert
-that `req` to ECS HTTP fields. This is a slight limitation for users of
-https://github.com/pinojs/pino-http[pino-http] which does this.
-
-
-[float]
-[[pino-ref]]
-=== Reference
-
-[float]
-[[pino-ref-ecsFormat]]
-==== `ecsFormat([options])`
-
-* `options` +{type-object}+ The following options are supported:
-** `convertErr` +{type-boolean}+ Whether to convert a logged `err` field to ECS error fields. *Default:* `true`.
-** `convertReqRes` +{type-boolean}+ Whether to logged `req` and `res` HTTP request and response fields to ECS HTTP, User agent, and URL fields. *Default:* `false`.
-** `apmIntegration` +{type-boolean}+ Whether to enable APM agent integration. *Default:* `true`.
-** `serviceName` +{type-string}+ A "service.name" value. If specified this overrides any value from an active APM agent.
-** `serviceVersion` +{type-string}+ A "service.version" value. If specified this overrides any value from an active APM agent.
-** `serviceEnvironment` +{type-string}+ A "service.environment" value. If specified this overrides any value from an active APM agent.
-** `serviceNodeName` +{type-string}+ A "service.node.name" value. If specified this overrides any value from an active APM agent.
-** `eventDataset` +{type-string}+ A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
-
-Create options for `pino(...)` that configures ECS Logging format output.
diff --git a/docs/reference/index.md b/docs/reference/index.md
new file mode 100644
index 0000000..b05b883
--- /dev/null
+++ b/docs/reference/index.md
@@ -0,0 +1,34 @@
+---
+mapped_pages:
+ - https://www.elastic.co/guide/en/ecs-logging/nodejs/current/intro.html
+ - https://www.elastic.co/guide/en/ecs-logging/nodejs/current/index.html
+---
+
+# ECS Logging Node.js [intro]
+
+Node.js ECS loggers are formatter plugins for your favorite logging libraries. They make it easy to format your logs into ECS-compatible JSON. In combination with [filebeat](https://www.elastic.co/products/beats/filebeat) you can send your logs directly to Elasticsearch and leverage [Kibana’s Logs app](docs-content://solutions/observability/logs/explore-logs.md) to inspect all logs in one single place.
+
+The Node.js ECS logging formatters log structured JSON and support serialization of Error objects and HTTP Request and Response objects from Node.js core and popular web frameworks. A minimal log record includes the following fields:
+
+```json
+{
+ "@timestamp": "2021-01-13T21:32:38.095Z",
+ "log.level": "info",
+ "message": "hi",
+ "ecs.version": "8.10.0"
+}
+```
+
+::::{tip}
+Want to learn more about ECS, ECS logging, and other available language plugins? See the [ECS logging guide](ecs-logging://docs/reference/intro.md).
+::::
+
+
+Ready to jump into Node.js ECS logging?
+
+* [ECS Logging with Pino](/reference/pino.md)
+* [ECS Logging with Winston](/reference/winston.md)
+* [ECS Logging with Morgan](/reference/morgan.md)
+
+If you’d like to try out a tutorial using Node.js ECS logging with winston, see [Ingest logs from a Node.js web application using Filebeat](docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-logs-from-nodejs-web-application-using-filebeat.md).
+
diff --git a/docs/reference/morgan.md b/docs/reference/morgan.md
new file mode 100644
index 0000000..c461eaa
--- /dev/null
+++ b/docs/reference/morgan.md
@@ -0,0 +1,283 @@
+---
+mapped_pages:
+ - https://www.elastic.co/guide/en/ecs-logging/nodejs/current/morgan.html
+---
+
+# ECS Logging with Morgan [morgan]
+
+This Node.js package provides a formatter for the [morgan](https://github.com/expressjs/morgan#readme) logging middleware — commonly used with Express — compatible with [Elastic Common Schema (ECS) logging](ecs-logging://docs/reference/intro.md). In combination with the [Filebeat](https://www.elastic.co/beats/filebeat) shipper, you can [monitor all your logs](https://www.elastic.co/log-monitoring) in one place in the Elastic Stack.
+
+
+## Setup [_setup_3]
+
+### Step 1: Install [morgan-setup-step-1]
+
+```cmd
+$ npm install @elastic/ecs-morgan-format
+```
+
+
+### Step 2: Configure [morgan-setup-step-2]
+
+```js
+const app = require('express')();
+const morgan = require('morgan');
+const { ecsFormat } = require('@elastic/ecs-morgan-format');
+
+app.use(morgan(ecsFormat(/* options */))); <1>
+
+// ...
+app.get('/', function (req, res) {
+ res.send('hello, world!');
+})
+app.listen(3000);
+```
+
+1. Pass the ECS formatter to `morgan()`.
+
+
+
+### Step 3: Configure Filebeat [morgan-setup-step-3]
+
+The best way to collect the logs once they are ECS-formatted is with [Filebeat](beats://docs/reference/filebeat/filebeat-overview.md):
+
+:::::::{tab-set}
+
+::::::{tab-item} Log file
+1. Follow the [Filebeat quick start](beats://docs/reference/filebeat/filebeat-installation-configuration.md)
+2. Add the following configuration to your `filebeat.yaml` file.
+
+For Filebeat 7.16+
+
+```yaml
+filebeat.inputs:
+- type: filestream <1>
+ paths: /path/to/logs.json
+ parsers:
+ - ndjson:
+ overwrite_keys: true <2>
+ add_error_key: true <3>
+ expand_keys: true <4>
+
+processors: <5>
+ - add_host_metadata: ~
+ - add_cloud_metadata: ~
+ - add_docker_metadata: ~
+ - add_kubernetes_metadata: ~
+```
+
+1. Use the filestream input to read lines from active log files.
+2. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+3. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+4. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+5. Processors enhance your data. See [processors](beats://docs/reference/filebeat/filtering-enhancing-data.md) to learn more.
+
+
+For Filebeat < 7.16
+
+```yaml
+filebeat.inputs:
+- type: log
+ paths: /path/to/logs.json
+ json.keys_under_root: true
+ json.overwrite_keys: true
+ json.add_error_key: true
+ json.expand_keys: true
+
+processors:
+- add_host_metadata: ~
+- add_cloud_metadata: ~
+- add_docker_metadata: ~
+- add_kubernetes_metadata: ~
+```
+::::::
+
+::::::{tab-item} Kubernetes
+1. Make sure your application logs to stdout/stderr.
+2. Follow the [Run Filebeat on Kubernetes](beats://docs/reference/filebeat/running-on-kubernetes.md) guide.
+3. Enable [hints-based autodiscover](beats://docs/reference/filebeat/configuration-autodiscover-hints.md) (uncomment the corresponding section in `filebeat-kubernetes.yaml`).
+4. Add these annotations to your pods that log using ECS loggers. This will make sure the logs are parsed appropriately.
+
+```yaml
+annotations:
+ co.elastic.logs/json.overwrite_keys: true <1>
+ co.elastic.logs/json.add_error_key: true <2>
+ co.elastic.logs/json.expand_keys: true <3>
+```
+
+1. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+2. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+3. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+::::::
+
+::::::{tab-item} Docker
+1. Make sure your application logs to stdout/stderr.
+2. Follow the [Run Filebeat on Docker](beats://docs/reference/filebeat/running-on-docker.md) guide.
+3. Enable [hints-based autodiscover](beats://docs/reference/filebeat/configuration-autodiscover-hints.md).
+4. Add these labels to your containers that log using ECS loggers. This will make sure the logs are parsed appropriately.
+
+```yaml
+labels:
+ co.elastic.logs/json.overwrite_keys: true <1>
+ co.elastic.logs/json.add_error_key: true <2>
+ co.elastic.logs/json.expand_keys: true <3>
+```
+
+1. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+2. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+3. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+::::::
+
+:::::::
+For more information, see the [Filebeat reference](beats://docs/reference/filebeat/configuring-howto-filebeat.md).
+
+
+## Usage [morgan-usage]
+
+```js
+const app = require('express')();
+const morgan = require('morgan');
+const { ecsFormat } = require('@elastic/ecs-morgan-format');
+
+app.use(morgan(ecsFormat(/* options */))); <1>
+
+app.get('/', function (req, res) {
+ res.send('hello, world!');
+})
+app.get('/error', function (req, res, next) {
+ next(new Error('boom'));
+})
+
+app.listen(3000)
+```
+
+1. See available options [below](#morgan-ref).
+
+
+Running this script (the full example is [here](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-morgan-format/examples/express.js)) and making a request (via `curl -i localhost:3000/`) will produce log output similar to the following:
+
+```cmd
+% node examples/express.js | jq . # piping to jq for pretty-printing
+{
+ "@timestamp": "2021-01-16T00:03:23.279Z",
+ "log.level": "info",
+ "message": "::1 - - [16/Jan/2021:00:03:23 +0000] \"GET / HTTP/1.1\" 200 13 \"-\" \"curl/7.64.1\"",
+ "ecs.version": "8.10.0",
+ "http": {
+ "version": "1.1",
+ "request": {
+ "method": "GET",
+ "headers": {
+ "host": "localhost:3000",
+ "accept": "*/*"
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "x-powered-by": "Express",
+ "content-type": "text/html; charset=utf-8",
+ "etag": "W/\"d-HwnTDHB9U/PRbFMN1z1wps51lqk\""
+ },
+ "body": {
+ "bytes": 13
+ }
+ }
+ },
+ "url": {
+ "path": "/",
+ "domain": "localhost",
+ "full": "http://localhost:3000/"
+ },
+ "user_agent": {
+ "original": "curl/7.64.1"
+ }
+}
+```
+
+
+## Format options [morgan-format-options]
+
+You can pass any [`format` argument](https://github.com/expressjs/morgan#morganformat-options) you would normally pass to `morgan()`, and the log "message" field will use the specified format. The default is [`combined`](https://github.com/expressjs/morgan#combined).
+
+```js
+const app = require('express')();
+const morgan = require('morgan');
+const { ecsFormat } = require('@elastic/ecs-morgan-format');
+
+app.use(morgan(ecsFormat({ format: 'tiny' }))); <1>
+// ...
+```
+
+1. If "format" is the only option you are using, you may pass it as `ecsFormat('tiny')`.
+
+
+
+## log.level [morgan-log-level]
+
+The `log.level` field will be "error" for response codes >= 500, otherwise "info". For example, running [examples/express.js](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-morgan-format/examples/express.js) again, a `curl -i localhost:3000/error` will yield:
+
+```cmd
+% node examples/express.js | jq .
+{
+ "@timestamp": "2021-01-18T17:52:12.810Z",
+ "log.level": "error",
+ "message": "::1 - - [18/Jan/2021:17:52:12 +0000] \"GET /error HTTP/1.1\" 500 1416 \"-\" \"curl/7.64.1\"",
+ "http": {
+ "response": {
+ "status_code": 500,
+ ...
+```
+
+
+## Log correlation with APM [morgan-apm]
+
+This ECS log formatter integrates with [Elastic APM](https://www.elastic.co/apm). If your Node app is using the [Node.js Elastic APM Agent](apm-agent-nodejs://docs/reference/index.md), then a number of fields are added to log records to correlate between APM services or traces and logging data:
+
+* Log statements (e.g. `logger.info(...)`) called when there is a current tracing span will include [tracing fields](ecs://docs/reference/ecs-tracing.md) — `trace.id`, `transaction.id`.
+* A number of service identifier fields determined by or configured on the APM agent allow cross-linking between services and logs in Kibana — `service.name`, `service.version`, `service.environment`, `service.node.name`.
+* `event.dataset` enables [log rate anomaly detection](docs-content://solutions/observability/logs/inspect-log-anomalies.md) in the Elastic Observability app.
+
+For example, running [examples/express-with-apm.js](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-morgan-format/examples/express-with-apm.js) and `curl -i localhost:3000/` results in a log record with the following:
+
+```cmd
+% node examples/express-with-apm.js | jq .
+{
+ // The same fields as before, plus:
+ "service.name": "express-with-elastic-apm",
+ "service.version": "1.1.0",
+ "service.environment": "development",
+ "event.dataset": "express-with-elastic-apm",
+ "trace.id": "116d46f667a7600deed9c41fa015f7de",
+ "transaction.id": "b84fb72d7bf42866"
+}
+```
+
+These IDs match trace data reported by the APM agent.
+
+Integration with Elastic APM can be explicitly disabled via the `apmIntegration: false` option, for example:
+
+```js
+app.use(morgan(ecsFormat({ apmIntegration: false })));
+```
+
+
+## Reference [morgan-ref]
+
+
+### `ecsFormat([options])` [morgan-ref-ecsFormat]
+
+* `options` `{type-object}` The following options are supported:
+
+ * `format` `{type-string}` A format **name** (e.g. *combined*), format function (e.g. `morgan.combined`), or a format string (e.g. *:method :url :status*). This is used to format the "message" field. Defaults to `morgan.combined`.
+ * `convertErr` `{type-boolean}` Whether to convert a logged `err` field to ECS error fields. **Default:** `true`.
+ * `apmIntegration` `{type-boolean}` Whether to enable APM agent integration. **Default:** `true`.
+ * `serviceName` `{type-string}` A "service.name" value. If specified this overrides any value from an active APM agent.
+ * `serviceVersion` `{type-string}` A "service.version" value. If specified this overrides any value from an active APM agent.
+ * `serviceEnvironment` `{type-string}` A "service.environment" value. If specified this overrides any value from an active APM agent.
+ * `serviceNodeName` `{type-string}` A "service.node.name" value. If specified this overrides any value from an active APM agent.
+ * `eventDataset` `{type-string}` A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
+
+
+Create a formatter for morgan that emits in ECS Logging format.
+
diff --git a/docs/reference/pino.md b/docs/reference/pino.md
new file mode 100644
index 0000000..1697d72
--- /dev/null
+++ b/docs/reference/pino.md
@@ -0,0 +1,324 @@
+---
+mapped_pages:
+ - https://www.elastic.co/guide/en/ecs-logging/nodejs/current/pino.html
+---
+
+# ECS Logging with Pino [pino]
+
+This Node.js package provides a formatter for the [pino](https://getpino.io) logger, compatible with [Elastic Common Schema (ECS) logging](ecs-logging://docs/reference/intro.md). In combination with the [Filebeat](https://www.elastic.co/beats/filebeat) shipper, you can [monitor all your logs](https://www.elastic.co/log-monitoring) in one place in the Elastic Stack. `pino` 6.x, 7.x, and 8.x versions are supported.
+
+
+## Setup [_setup]
+
+
+### Step 1: Install [pino-setup-step-1]
+
+```cmd
+$ npm install @elastic/ecs-pino-format
+```
+
+
+### Step 2: Configure [pino-setup-step-2]
+
+```js
+const { ecsFormat } = require('@elastic/ecs-pino-format');
+const pino = require('pino');
+
+const log = pino(ecsFormat(/* options */)); <1>
+log.info('hi');
+log.error({ err: new Error('boom') }, 'oops there is a problem');
+// ...
+```
+
+1. This will [configure](https://getpino.io/#/docs/api?id=options) Pino’s `formatters`, `messageKey` and `timestamp` options.
+
+
+See usage discussion and examples below.
+
+
+### Step 3: Configure Filebeat [pino-setup-step-3]
+
+The best way to collect the logs once they are ECS-formatted is with [Filebeat](beats://docs/reference/filebeat/filebeat-overview.md):
+
+:::::::{tab-set}
+
+::::::{tab-item} Log file
+1. Follow the [Filebeat quick start](beats://docs/reference/filebeat/filebeat-installation-configuration.md)
+2. Add the following configuration to your `filebeat.yaml` file.
+
+For Filebeat 7.16+
+
+```yaml
+filebeat.inputs:
+- type: filestream <1>
+ paths: /path/to/logs.json
+ parsers:
+ - ndjson:
+ overwrite_keys: true <2>
+ add_error_key: true <3>
+ expand_keys: true <4>
+
+processors: <5>
+ - add_host_metadata: ~
+ - add_cloud_metadata: ~
+ - add_docker_metadata: ~
+ - add_kubernetes_metadata: ~
+```
+
+1. Use the filestream input to read lines from active log files.
+2. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+3. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+4. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+5. Processors enhance your data. See [processors](beats://docs/reference/filebeat/filtering-enhancing-data.md) to learn more.
+
+
+For Filebeat < 7.16
+
+```yaml
+filebeat.inputs:
+- type: log
+ paths: /path/to/logs.json
+ json.keys_under_root: true
+ json.overwrite_keys: true
+ json.add_error_key: true
+ json.expand_keys: true
+
+processors:
+- add_host_metadata: ~
+- add_cloud_metadata: ~
+- add_docker_metadata: ~
+- add_kubernetes_metadata: ~
+```
+::::::
+
+::::::{tab-item} Kubernetes
+1. Make sure your application logs to stdout/stderr.
+2. Follow the [Run Filebeat on Kubernetes](beats://docs/reference/filebeat/running-on-kubernetes.md) guide.
+3. Enable [hints-based autodiscover](beats://docs/reference/filebeat/configuration-autodiscover-hints.md) (uncomment the corresponding section in `filebeat-kubernetes.yaml`).
+4. Add these annotations to your pods that log using ECS loggers. This will make sure the logs are parsed appropriately.
+
+```yaml
+annotations:
+ co.elastic.logs/json.overwrite_keys: true <1>
+ co.elastic.logs/json.add_error_key: true <2>
+ co.elastic.logs/json.expand_keys: true <3>
+```
+
+1. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+2. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+3. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+::::::
+
+::::::{tab-item} Docker
+1. Make sure your application logs to stdout/stderr.
+2. Follow the [Run Filebeat on Docker](beats://docs/reference/filebeat/running-on-docker.md) guide.
+3. Enable [hints-based autodiscover](beats://docs/reference/filebeat/configuration-autodiscover-hints.md).
+4. Add these labels to your containers that log using ECS loggers. This will make sure the logs are parsed appropriately.
+
+```yaml
+labels:
+ co.elastic.logs/json.overwrite_keys: true <1>
+ co.elastic.logs/json.add_error_key: true <2>
+ co.elastic.logs/json.expand_keys: true <3>
+```
+
+1. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+2. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+3. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+::::::
+
+:::::::
+For more information, see the [Filebeat reference](beats://docs/reference/filebeat/configuring-howto-filebeat.md).
+
+
+## Usage [pino-usage]
+
+```js
+const { ecsFormat } = require('@elastic/ecs-pino-format');
+const pino = require('pino');
+
+const log = pino(ecsFormat(/* options */)); <1>
+log.info('Hello world');
+
+const child = log.child({ module: 'foo' });
+child.warn('From child');
+```
+
+1. See available options [below](#pino-ref).
+
+
+Running this will produce log output similar to the following:
+
+```cmd
+{"log.level":"info","@timestamp":"2023-10-14T02:07:47.901Z","process.pid":56645,"host.hostname":"pink.local","ecs.version":"8.10.0","message":"Hello world"}
+{"log.level":"warn","@timestamp":"2023-10-14T02:07:47.901Z","process.pid":56645,"host.hostname":"pink.local","ecs.version":"8.10.0","module":"foo","message":"From child"}
+```
+
+
+## Error Logging [pino-error-logging]
+
+By default, the formatter will convert an `err` field that is an Error instance to [ECS Error fields](ecs://docs/reference/ecs-error.md). For example:
+
+```js
+const { ecsFormat } = require('@elastic/ecs-pino-format');
+const pino = require('pino');
+const log = pino(ecsFormat());
+
+const myErr = new Error('boom');
+log.info({ err: myErr }, 'oops');
+```
+
+will yield (pretty-printed for readability):
+
+```cmd
+% node examples/error.js | jq .
+{
+ "log.level": "info",
+ "@timestamp": "2021-01-26T17:02:23.697Z",
+ ...
+ "error": {
+ "type": "Error",
+ "message": "boom",
+ "stack_trace": "Error: boom\n at Object. (..."
+ },
+ "message": "oops"
+}
+```
+
+This is analogous to and overrides [Pino’s default err serializer](https://getpino.io/#/docs/api?id=serializers-object). Special handling of the `err` field can be disabled via the `convertErr: false` option:
+
+```js
+const log = pino(ecsFormat({ convertErr: false }));
+```
+
+
+## HTTP Request and Response Logging [pino-http-logging]
+
+With the `convertReqRes: true` option, the formatter will automatically convert Node.js core [request](https://nodejs.org/api/http.md#http_class_http_incomingmessage) and [response](https://nodejs.org/api/http.md#http_class_http_serverresponse) objects when passed as the `req` and `res` fields, respectively. (This option replaces the usage of `req` and `res` [Pino serializers](https://getpino.io/#/docs/api?id=pinostdserializers-object).)
+
+```js
+const http = require('http');
+const { ecsFormat } = require('@elastic/ecs-pino-format');
+const pino = require('pino');
+
+const log = pino(ecsFormat({ convertReqRes: true })); <1>
+
+const server = http.createServer(function handler (req, res) {
+ res.setHeader('Foo', 'Bar');
+ res.end('ok');
+ log.info({ req, res }, 'handled request'); <2>
+});
+
+server.listen(3000, () => {
+ log.info('listening at http://localhost:3000');
+}
+```
+
+1. use `convertReqRes` option
+2. log with `req` and/or `res` fields
+
+
+This will produce logs with request and response info using [ECS HTTP fields](ecs://docs/reference/ecs-http.md). For example:
+
+```cmd
+% node examples/http.js | jq . # using jq for pretty printing
+... # run 'curl http://localhost:3000/'
+{
+ "log.level": "info",
+ "@timestamp": "2023-10-14T02:10:14.477Z",
+ "process.pid": 56697,
+ "host.hostname": "pink.local",
+ "ecs.version": "8.10.0",
+ "http": {
+ "version": "1.1",
+ "request": {
+ "method": "GET",
+ "headers": {
+ "host": "localhost:3000",
+ "user-agent": "curl/8.1.2",
+ "accept": "*/*"
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "foo": "Bar"
+ }
+ }
+ },
+ "url": {
+ "full": "http://localhost:3000/",
+ "path": "/"
+ },
+ "client": {
+ "address": "::ffff:127.0.0.1",
+ "ip": "::ffff:127.0.0.1",
+ "port": 49504
+ },
+ "user_agent": {
+ "original": "curl/8.1.2"
+ },
+ "message": "handled request"
+}
+```
+
+The [examples/ directory](https://github.com/elastic/ecs-logging-nodejs/tree/main/packages/ecs-pino-format/examples) shows sample programs using request and response logging: [with Express](https://github.com/elastic/ecs-logging-nodejs/tree/main/packages/ecs-pino-format/examples/express-simple.js), [with the pino-http middleware package](https://github.com/elastic/ecs-logging-nodejs/tree/main/packages/ecs-pino-format/examples/express-with-pino-http.js), etc.
+
+
+## Log Correlation with APM [pino-apm]
+
+This ECS log formatter integrates with [Elastic APM](https://www.elastic.co/apm). If your Node app is using the [Node.js Elastic APM Agent](apm-agent-nodejs://docs/reference/index.md), then a number of fields are added to log records to correlate between APM services or traces and logging data:
+
+* Log statements (e.g. `logger.info(...)`) called when there is a current tracing span will include [tracing fields](ecs://docs/reference/ecs-tracing.md) — `trace.id`, `transaction.id`, `span.id`.
+* A number of service identifier fields determined by or configured on the APM agent allow cross-linking between services and logs in Kibana — `service.name`, `service.version`, `service.environment`, `service.node.name`.
+* `event.dataset` enables [log rate anomaly detection](docs-content://solutions/observability/logs/inspect-log-anomalies.md) in the Elastic Observability app.
+
+For example, running [examples/http-with-elastic-apm.js](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-pino-format/examples/http-with-elastic-apm.js) and `curl -i localhost:3000/` results in a log record with the following:
+
+```cmd
+% node examples/http-with-elastic-apm.js | jq .
+...
+ "service.name": "http-with-elastic-apm",
+ "service.version": "1.4.0",
+ "service.environment": "development",
+ "event.dataset": "http-with-elastic-apm",
+ "trace.id": "9f338eae7211b7993b98929046aed21d",
+ "transaction.id": "2afbef5642cc7a3f",
+...
+```
+
+These IDs match trace data reported by the APM agent.
+
+Integration with Elastic APM can be explicitly disabled via the `apmIntegration: false` option, for example:
+
+```js
+const log = pino(ecsFormat({ apmIntegration: false }));
+```
+
+
+## Limitations and Considerations [pino-considerations]
+
+The [ecs-logging spec](https://github.com/elastic/ecs-logging/tree/main/spec) suggests that the first three fields in log records must be `@timestamp`, `log.level`, and `message`. Pino does not provide a mechanism to put the `message` field near the front. Given that ordering of ecs-logging fields is for **human readability** and does not affect interoperability, this is not considered a significant concern.
+
+The hooks that Pino currently provides do not enable this package to convert fields passed to `.child({ ... })`. This means that, even with the `convertReqRes` option, a call to `.child({ req })` will **not** convert that `req` to ECS HTTP fields. This is a slight limitation for users of [pino-http](https://github.com/pinojs/pino-http) which does this.
+
+
+## Reference [pino-ref]
+
+
+### `ecsFormat([options])` [pino-ref-ecsFormat]
+
+* `options` `{type-object}` The following options are supported:
+
+ * `convertErr` `{type-boolean}` Whether to convert a logged `err` field to ECS error fields. **Default:** `true`.
+ * `convertReqRes` `{type-boolean}` Whether to logged `req` and `res` HTTP request and response fields to ECS HTTP, User agent, and URL fields. **Default:** `false`.
+ * `apmIntegration` `{type-boolean}` Whether to enable APM agent integration. **Default:** `true`.
+ * `serviceName` `{type-string}` A "service.name" value. If specified this overrides any value from an active APM agent.
+ * `serviceVersion` `{type-string}` A "service.version" value. If specified this overrides any value from an active APM agent.
+ * `serviceEnvironment` `{type-string}` A "service.environment" value. If specified this overrides any value from an active APM agent.
+ * `serviceNodeName` `{type-string}` A "service.node.name" value. If specified this overrides any value from an active APM agent.
+ * `eventDataset` `{type-string}` A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
+
+
+Create options for `pino(...)` that configures ECS Logging format output.
+
diff --git a/docs/reference/toc.yml b/docs/reference/toc.yml
new file mode 100644
index 0000000..2d6afdd
--- /dev/null
+++ b/docs/reference/toc.yml
@@ -0,0 +1,5 @@
+toc:
+ - file: index.md
+ - file: pino.md
+ - file: winston.md
+ - file: morgan.md
\ No newline at end of file
diff --git a/docs/reference/winston.md b/docs/reference/winston.md
new file mode 100644
index 0000000..f006023
--- /dev/null
+++ b/docs/reference/winston.md
@@ -0,0 +1,397 @@
+---
+mapped_pages:
+ - https://www.elastic.co/guide/en/ecs-logging/nodejs/current/winston.html
+---
+
+# ECS Logging with Winston [winston]
+
+This Node.js package provides a formatter for the [winston](https://github.com/winstonjs/winston#readme) logger, compatible with [Elastic Common Schema (ECS) logging](ecs-logging://docs/reference/intro.md). In combination with the [Filebeat](https://www.elastic.co/beats/filebeat) shipper, you can [monitor all your logs](https://www.elastic.co/log-monitoring) in one place in the Elastic Stack. `winston` 3.x versions >=3.3.3 are supported.
+
+
+## Setup [_setup_2]
+
+
+### Step 1: Install [winston-setup-step-1]
+
+```cmd
+$ npm install @elastic/ecs-winston-format
+```
+
+
+### Step 2: Configure [winston-setup-step-2]
+
+```js
+const winston = require('winston');
+const { ecsFormat } = require('@elastic/ecs-winston-format');
+
+const logger = winston.createLogger({
+ format: ecsFormat(/* options */), <1>
+ transports: [
+ new winston.transports.Console()
+ ]
+});
+
+logger.info('hi');
+logger.error('oops there is a problem', { err: new Error('boom') });
+```
+
+1. Pass the ECS formatter to winston here.
+
+
+
+### Step 3: Configure Filebeat [winston-setup-step-3]
+
+The best way to collect the logs once they are ECS-formatted is with [Filebeat](beats://docs/reference/filebeat/filebeat-overview.md):
+
+:::::::{tab-set}
+
+::::::{tab-item} Log file
+1. Follow the [Filebeat quick start](beats://docs/reference/filebeat/filebeat-installation-configuration.md)
+2. Add the following configuration to your `filebeat.yaml` file.
+
+For Filebeat 7.16+
+
+```yaml
+filebeat.inputs:
+- type: filestream <1>
+ paths: /path/to/logs.json
+ parsers:
+ - ndjson:
+ overwrite_keys: true <2>
+ add_error_key: true <3>
+ expand_keys: true <4>
+
+processors: <5>
+ - add_host_metadata: ~
+ - add_cloud_metadata: ~
+ - add_docker_metadata: ~
+ - add_kubernetes_metadata: ~
+```
+
+1. Use the filestream input to read lines from active log files.
+2. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+3. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+4. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+5. Processors enhance your data. See [processors](beats://docs/reference/filebeat/filtering-enhancing-data.md) to learn more.
+
+
+For Filebeat < 7.16
+
+```yaml
+filebeat.inputs:
+- type: log
+ paths: /path/to/logs.json
+ json.keys_under_root: true
+ json.overwrite_keys: true
+ json.add_error_key: true
+ json.expand_keys: true
+
+processors:
+- add_host_metadata: ~
+- add_cloud_metadata: ~
+- add_docker_metadata: ~
+- add_kubernetes_metadata: ~
+```
+::::::
+
+::::::{tab-item} Kubernetes
+1. Make sure your application logs to stdout/stderr.
+2. Follow the [Run Filebeat on Kubernetes](beats://docs/reference/filebeat/running-on-kubernetes.md) guide.
+3. Enable [hints-based autodiscover](beats://docs/reference/filebeat/configuration-autodiscover-hints.md) (uncomment the corresponding section in `filebeat-kubernetes.yaml`).
+4. Add these annotations to your pods that log using ECS loggers. This will make sure the logs are parsed appropriately.
+
+```yaml
+annotations:
+ co.elastic.logs/json.overwrite_keys: true <1>
+ co.elastic.logs/json.add_error_key: true <2>
+ co.elastic.logs/json.expand_keys: true <3>
+```
+
+1. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+2. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+3. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+::::::
+
+::::::{tab-item} Docker
+1. Make sure your application logs to stdout/stderr.
+2. Follow the [Run Filebeat on Docker](beats://docs/reference/filebeat/running-on-docker.md) guide.
+3. Enable [hints-based autodiscover](beats://docs/reference/filebeat/configuration-autodiscover-hints.md).
+4. Add these labels to your containers that log using ECS loggers. This will make sure the logs are parsed appropriately.
+
+```yaml
+labels:
+ co.elastic.logs/json.overwrite_keys: true <1>
+ co.elastic.logs/json.add_error_key: true <2>
+ co.elastic.logs/json.expand_keys: true <3>
+```
+
+1. Values from the decoded JSON object overwrite the fields that {{filebeat}} normally adds (type, source, offset, etc.) in case of conflicts.
+2. {{filebeat}} adds an "error.message" and "error.type: json" key in case of JSON unmarshalling errors.
+3. {{filebeat}} will recursively de-dot keys in the decoded JSON, and expand them into a hierarchical object structure.
+::::::
+
+:::::::
+For more information, see the [Filebeat reference](beats://docs/reference/filebeat/configuring-howto-filebeat.md).
+
+::::{note}
+You might like to try out our tutorial using Node.js ECS logging with winston: [Ingest logs from a Node.js web application using Filebeat](docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-logs-from-nodejs-web-application-using-filebeat.md).
+::::
+
+
+
+## Usage [winston-usage]
+
+```js
+const winston = require('winston');
+const { ecsFormat } = require('@elastic/ecs-winston-format');
+
+const logger = winston.createLogger({
+ level: 'info',
+ format: ecsFormat(/* options */), <1>
+ transports: [
+ new winston.transports.Console()
+ ]
+});
+
+logger.info('hi');
+logger.error('oops there is a problem', { foo: 'bar' });
+```
+
+1. See available options [below](#winston-ref).
+
+
+Running this script (available [here](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/basic.js)) will produce log output similar to the following:
+
+```cmd
+% node examples/basic.js
+{"@timestamp":"2023-10-14T02:14:17.302Z","log.level":"info","message":"hi","ecs.version":"8.10.0"}
+{"@timestamp":"2023-10-14T02:14:17.304Z","log.level":"error","message":"oops there is a problem","ecs.version":"8.10.0","foo":"bar"}
+```
+
+The formatter handles serialization to JSON, so you don’t need to add the [json](https://github.com/winstonjs/logform#json) formatter. As well, a timestamp is automatically generated by the formatter, so you don’t need to add the [timestamp](https://github.com/winstonjs/logform#timestamp) formatter.
+
+
+## Error logging [winston-error-logging]
+
+By default, the formatter will convert an `err` meta field that is an Error instance to [ECS Error fields](ecs://docs/reference/ecs-error.md). For [example](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/error.js):
+
+```js
+const winston = require('winston');
+const { ecsFormat } = require('@elastic/ecs-winston-format');
+const logger = winston.createLogger({
+ format: ecsFormat(),
+ transports: [
+ new winston.transports.Console()
+ ]
+});
+
+const myErr = new Error('boom');
+logger.info('oops', { err: myErr });
+```
+
+will yield (pretty-printed for readability):
+
+```cmd
+% node examples/error.js | jq .
+{
+ "@timestamp": "2021-01-26T17:25:07.983Z",
+ "log.level": "info",
+ "message": "oops",
+ "error": {
+ "type": "Error",
+ "message": "boom",
+ "stack_trace": "Error: boom\n at Object. (..."
+ },
+ "ecs.version": "8.10.0"
+}
+```
+
+Special handling of the `err` meta field can be disabled via the `convertErr: false` option:
+
+```js
+...
+const logger = winston.createLogger({
+ format: ecsFormat({ convertErr: false }),
+...
+```
+
+
+## HTTP Request and Response Logging [winston-http-logging]
+
+With the `convertReqRes: true` option, the formatter will automatically convert Node.js core [request](https://nodejs.org/api/http.md#http_class_http_incomingmessage) and [response](https://nodejs.org/api/http.md#http_class_http_serverresponse) objects when passed as the `req` and `res` meta fields, respectively.
+
+```js
+const http = require('http');
+const winston = require('winston');
+const { ecsFormat } = require('@elastic/ecs-winston-format');
+
+const logger = winston.createLogger({
+ level: 'info',
+ format: ecsFormat({ convertReqRes: true }), <1>
+ transports: [
+ new winston.transports.Console()
+ ]
+});
+
+const server = http.createServer(handler);
+server.listen(3000, () => {
+ logger.info('listening at http://localhost:3000')
+});
+
+function handler (req, res) {
+ res.setHeader('Foo', 'Bar');
+ res.end('ok');
+ logger.info('handled request', { req, res }); <2>
+}
+```
+
+1. use `convertReqRes` option
+2. log `req` and/or `res` meta fields
+
+
+This will produce logs with request and response info using [ECS HTTP fields](ecs://docs/reference/ecs-http.md). For [example](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/http.js):
+
+```cmd
+% node examples/http.js | jq . # using jq for pretty printing
+... # run 'curl http://localhost:3000/'
+{
+ "@timestamp": "2023-10-14T02:15:54.768Z",
+ "log.level": "info",
+ "message": "handled request",
+ "http": {
+ "version": "1.1",
+ "request": {
+ "method": "GET",
+ "headers": {
+ "host": "localhost:3000",
+ "user-agent": "curl/8.1.2",
+ "accept": "*/*"
+ }
+ },
+ "response": {
+ "status_code": 200,
+ "headers": {
+ "foo": "Bar"
+ }
+ }
+ },
+ "url": {
+ "path": "/",
+ "full": "http://localhost:3000/"
+ },
+ "client": {
+ "address": "::ffff:127.0.0.1",
+ "ip": "::ffff:127.0.0.1",
+ "port": 49538
+ },
+ "user_agent": {
+ "original": "curl/8.1.2"
+ },
+ "ecs.version": "8.10.0"
+}
+```
+
+
+## Log Correlation with APM [winston-apm]
+
+This ECS log formatter integrates with [Elastic APM](https://www.elastic.co/apm). If your Node app is using the [Node.js Elastic APM Agent](apm-agent-nodejs://docs/reference/index.md), then a number of fields are added to log records to correlate between APM services or traces and logging data:
+
+* Log statements (e.g. `logger.info(...)`) called when there is a current tracing span will include [tracing fields](ecs://docs/reference/ecs-tracing.md) — `trace.id`, `transaction.id`, `span.id`.
+* A number of service identifier fields determined by or configured on the APM agent allow cross-linking between services and logs in Kibana — `service.name`, `service.version`, `service.environment`, `service.node.name`.
+* `event.dataset` enables [log rate anomaly detection](docs-content://solutions/observability/logs/inspect-log-anomalies.md) in the Elastic Observability app.
+
+For example, running [examples/http-with-elastic-apm.js](https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/http-with-elastic-apm.js) and `curl -i localhost:3000/` results in a log record with the following:
+
+```cmd
+% node examples/http-with-elastic-apm.js | jq .
+...
+ "service.name": "http-with-elastic-apm",
+ "service.version": "1.4.0",
+ "service.environment": "development",
+ "event.dataset": "http-with-elastic-apm"
+ "trace.id": "7fd75f0f33ff49aba85d060b46dcad7e",
+ "transaction.id": "6c97c7c1b468fa05"
+}
+```
+
+These IDs match trace data reported by the APM agent.
+
+Integration with Elastic APM can be explicitly disabled via the `apmIntegration: false` option, for example:
+
+```js
+const logger = winston.createLogger({
+ format: ecsFormat({ apmIntegration: false }),
+ // ...
+})
+```
+
+
+## Limitations and Considerations [winston-limitations]
+
+The [ecs-logging spec](https://github.com/elastic/ecs-logging/tree/main/spec) suggests that the first three fields in log records should be `@timestamp`, `log.level`, and `message`. As of version 1.5.0, this formatter does **not** follow this suggestion. It would be possible but would require creating a new Object in `ecsFields` for each log record. Given that ordering of ecs-logging fields is for **human readability** and does not affect interoperability, the decision was made to prefer performance.
+
+
+## Reference [winston-ref]
+
+
+### `ecsFormat([options])` [winston-ref-ecsFormat]
+
+* `options` `{type-object}` The following options are supported:
+
+ * `convertErr` `{type-boolean}` Whether to convert a logged `err` field to ECS error fields. **Default:** `true`.
+ * `convertReqRes` `{type-boolean}` Whether to logged `req` and `res` HTTP request and response fields to ECS HTTP, User agent, and URL fields. **Default:** `false`.
+ * `apmIntegration` `{type-boolean}` Whether to enable APM agent integration. **Default:** `true`.
+ * `serviceName` `{type-string}` A "service.name" value. If specified this overrides any value from an active APM agent.
+ * `serviceVersion` `{type-string}` A "service.version" value. If specified this overrides any value from an active APM agent.
+ * `serviceEnvironment` `{type-string}` A "service.environment" value. If specified this overrides any value from an active APM agent.
+ * `serviceNodeName` `{type-string}` A "service.node.name" value. If specified this overrides any value from an active APM agent.
+ * `eventDataset` `{type-string}` A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
+
+
+Create a formatter for winston that emits in ECS Logging format. This is a single format that handles both [`ecsFields([options])`](#winston-ref-ecsFields) and [`ecsStringify([options])`](#winston-ref-ecsStringify). The following two are equivalent:
+
+```js
+const { ecsFormat, ecsFields, ecsStringify } = require('@elastic/ecs-winston-format');
+const winston = require('winston');
+
+const logger = winston.createLogger({
+ format: ecsFormat(/* options */),
+ // ...
+});
+
+const logger = winston.createLogger({
+ format: winston.format.combine(
+ ecsFields(/* options */),
+ ecsStringify()
+ ),
+ // ...
+});
+```
+
+
+### `ecsFields([options])` [winston-ref-ecsFields]
+
+* `options` `{type-object}` The following options are supported:
+
+ * `convertErr` `{type-boolean}` Whether to convert a logged `err` field to ECS error fields. **Default:** `true`.
+ * `convertReqRes` `{type-boolean}` Whether to logged `req` and `res` HTTP request and response fields to ECS HTTP, User agent, and URL fields. **Default:** `false`.
+ * `apmIntegration` `{type-boolean}` Whether to enable APM agent integration. **Default:** `true`.
+ * `serviceName` `{type-string}` A "service.name" value. If specified this overrides any value from an active APM agent.
+ * `serviceVersion` `{type-string}` A "service.version" value. If specified this overrides any value from an active APM agent.
+ * `serviceEnvironment` `{type-string}` A "service.environment" value. If specified this overrides any value from an active APM agent.
+ * `serviceNodeName` `{type-string}` A "service.node.name" value. If specified this overrides any value from an active APM agent.
+ * `eventDataset` `{type-string}` A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
+
+
+Create a formatter for winston that converts fields on the log record info object to ECS Logging format.
+
+
+### `ecsStringify([options])` [winston-ref-ecsStringify]
+
+Create a formatter for winston that stringifies/serializes the log record to JSON.
+
+This is similar to `logform.json()`. They both use the `safe-stable-stringify` package to produce the JSON. Some differences:
+
+* This stringifier skips serializing the `level` field, because it is not an ECS field.
+* Winston provides a `replacer` that converts bigints to strings The argument **for** doing so is that a **JavaScript** JSON parser looses precision when parsing a bigint. The argument against is that a BigInt changes type to a string rather than a number. For now this stringifier does not convert BitInts to strings.
+
diff --git a/docs/winston.asciidoc b/docs/winston.asciidoc
deleted file mode 100644
index 9a22108..0000000
--- a/docs/winston.asciidoc
+++ /dev/null
@@ -1,360 +0,0 @@
-[[winston]]
-== ECS Logging with Winston
-
-This Node.js package provides a formatter for the https://github.com/winstonjs/winston#readme[winston]
-logger, compatible with {ecs-logging-ref}/intro.html[Elastic Common Schema (ECS) logging].
-In combination with the https://www.elastic.co/beats/filebeat[Filebeat] shipper,
-you can https://www.elastic.co/log-monitoring[monitor all your logs] in one
-place in the Elastic Stack. `winston` 3.x versions >=3.3.3 are supported.
-
-
-[float]
-=== Setup
-
-[float]
-[[winston-setup-step-1]]
-==== Step 1: Install
-
-[source,cmd]
-----
-$ npm install @elastic/ecs-winston-format
-----
-
-[float]
-[[winston-setup-step-2]]
-==== Step 2: Configure
-
-[source,js]
-----
-const winston = require('winston');
-const { ecsFormat } = require('@elastic/ecs-winston-format');
-
-const logger = winston.createLogger({
- format: ecsFormat(/* options */), <1>
- transports: [
- new winston.transports.Console()
- ]
-});
-
-logger.info('hi');
-logger.error('oops there is a problem', { err: new Error('boom') });
-----
-<1> Pass the ECS formatter to winston here.
-
-
-[float]
-[[winston-setup-step-3]]
-==== Step 3: Configure Filebeat
-
-The best way to collect the logs once they are ECS-formatted is with {filebeat-ref}[Filebeat]:
-
-include::{ecs-repo-dir}/setup.asciidoc[tag=configure-filebeat]
-
-NOTE: You might like to try out our tutorial using Node.js ECS logging with winston: {cloud}/ec-getting-started-search-use-cases-node-logs.html[Ingest logs from a Node.js web application using Filebeat].
-
-[float]
-[[winston-usage]]
-=== Usage
-
-[source,js]
-----
-const winston = require('winston');
-const { ecsFormat } = require('@elastic/ecs-winston-format');
-
-const logger = winston.createLogger({
- level: 'info',
- format: ecsFormat(/* options */), <1>
- transports: [
- new winston.transports.Console()
- ]
-});
-
-logger.info('hi');
-logger.error('oops there is a problem', { foo: 'bar' });
-----
-<1> See available options <>.
-
-Running this script (available https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/basic.js[here]) will produce log output similar to the following:
-
-[source,cmd]
-----
-% node examples/basic.js
-{"@timestamp":"2023-10-14T02:14:17.302Z","log.level":"info","message":"hi","ecs.version":"8.10.0"}
-{"@timestamp":"2023-10-14T02:14:17.304Z","log.level":"error","message":"oops there is a problem","ecs.version":"8.10.0","foo":"bar"}
-----
-
-The formatter handles serialization to JSON, so you don't need to add the
-https://github.com/winstonjs/logform#json[json] formatter. As well, a
-timestamp is automatically generated by the formatter, so you don't need to add
-the https://github.com/winstonjs/logform#timestamp[timestamp] formatter.
-
-
-[float]
-[[winston-error-logging]]
-=== Error logging
-
-By default, the formatter will convert an `err` meta field that is an Error instance
-to {ecs-ref}/ecs-error.html[ECS Error fields].
-For https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/error.js[example]:
-
-[source,js]
-----
-const winston = require('winston');
-const { ecsFormat } = require('@elastic/ecs-winston-format');
-const logger = winston.createLogger({
- format: ecsFormat(), <1>
- transports: [
- new winston.transports.Console()
- ]
-});
-
-const myErr = new Error('boom');
-logger.info('oops', { err: myErr }); <2>
-----
-
-will yield (pretty-printed for readability):
-
-[source,cmd]
-----
-% node examples/error.js | jq .
-{
- "@timestamp": "2021-01-26T17:25:07.983Z",
- "log.level": "info",
- "message": "oops",
- "error": {
- "type": "Error",
- "message": "boom",
- "stack_trace": "Error: boom\n at Object. (..."
- },
- "ecs.version": "8.10.0"
-}
-----
-
-Special handling of the `err` meta field can be disabled via the `convertErr:
-false` option:
-
-[source,js]
-----
-...
-const logger = winston.createLogger({
- format: ecsFormat({ convertErr: false }),
-...
-----
-
-
-[float]
-[[winston-http-logging]]
-=== HTTP Request and Response Logging
-
-With the `convertReqRes: true` option, the formatter will automatically
-convert Node.js core https://nodejs.org/api/http.html#http_class_http_incomingmessage[request]
-and https://nodejs.org/api/http.html#http_class_http_serverresponse[response]
-objects when passed as the `req` and `res` meta fields, respectively.
-
-[source,js]
-----
-const http = require('http');
-const winston = require('winston');
-const { ecsFormat } = require('@elastic/ecs-winston-format');
-
-const logger = winston.createLogger({
- level: 'info',
- format: ecsFormat({ convertReqRes: true }), <1>
- transports: [
- new winston.transports.Console()
- ]
-});
-
-const server = http.createServer(handler);
-server.listen(3000, () => {
- logger.info('listening at http://localhost:3000')
-});
-
-function handler (req, res) {
- res.setHeader('Foo', 'Bar');
- res.end('ok');
- logger.info('handled request', { req, res }); <2>
-}
-----
-<1> use `convertReqRes` option
-<2> log `req` and/or `res` meta fields
-
-This will produce logs with request and response info using
-{ecs-ref}/ecs-http.html[ECS HTTP fields].
-For https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/http.js[example]:
-
-[source,cmd]
-----
-% node examples/http.js | jq . # using jq for pretty printing
-... # run 'curl http://localhost:3000/'
-{
- "@timestamp": "2023-10-14T02:15:54.768Z",
- "log.level": "info",
- "message": "handled request",
- "http": {
- "version": "1.1",
- "request": {
- "method": "GET",
- "headers": {
- "host": "localhost:3000",
- "user-agent": "curl/8.1.2",
- "accept": "*/*"
- }
- },
- "response": {
- "status_code": 200,
- "headers": {
- "foo": "Bar"
- }
- }
- },
- "url": {
- "path": "/",
- "full": "http://localhost:3000/"
- },
- "client": {
- "address": "::ffff:127.0.0.1",
- "ip": "::ffff:127.0.0.1",
- "port": 49538
- },
- "user_agent": {
- "original": "curl/8.1.2"
- },
- "ecs.version": "8.10.0"
-}
-----
-
-
-[float]
-[[winston-apm]]
-=== Log Correlation with APM
-
-This ECS log formatter integrates with https://www.elastic.co/apm[Elastic APM].
-If your Node app is using the {apm-node-ref}/intro.html[Node.js Elastic APM Agent],
-then a number of fields are added to log records to correlate between APM
-services or traces and logging data:
-
-- Log statements (e.g. `logger.info(...)`) called when there is a current
- tracing span will include {ecs-ref}/ecs-tracing.html[tracing fields] --
- `trace.id`, `transaction.id`, `span.id`.
-- A number of service identifier fields determined by or configured on the APM
- agent allow cross-linking between services and logs in Kibana --
- `service.name`, `service.version`, `service.environment`, `service.node.name`.
-- `event.dataset` enables {observability-guide}/inspect-log-anomalies.html[log
- rate anomaly detection] in the Elastic Observability app.
-
-For example, running https://github.com/elastic/ecs-logging-nodejs/blob/main/packages/ecs-winston-format/examples/http-with-elastic-apm.js[examples/http-with-elastic-apm.js] and `curl -i localhost:3000/` results in a log record with the following:
-
-[source,cmd]
-----
-% node examples/http-with-elastic-apm.js | jq .
-...
- "service.name": "http-with-elastic-apm",
- "service.version": "1.4.0",
- "service.environment": "development",
- "event.dataset": "http-with-elastic-apm"
- "trace.id": "7fd75f0f33ff49aba85d060b46dcad7e",
- "transaction.id": "6c97c7c1b468fa05"
-}
-----
-
-These IDs match trace data reported by the APM agent.
-
-Integration with Elastic APM can be explicitly disabled via the
-`apmIntegration: false` option, for example:
-
-[source,js]
-----
-const logger = winston.createLogger({
- format: ecsFormat({ apmIntegration: false }),
- // ...
-})
-----
-
-[float]
-[[winston-limitations]]
-=== Limitations and Considerations
-
-The https://github.com/elastic/ecs-logging/tree/main/spec[ecs-logging spec]
-suggests that the first three fields in log records should be `@timestamp`,
-`log.level`, and `message`. As of version 1.5.0, this formatter does *not*
-follow this suggestion. It would be possible but would require creating a new
-Object in `ecsFields` for each log record. Given that ordering of ecs-logging
-fields is for *human readability* and does not affect interoperability, the
-decision was made to prefer performance.
-
-[float]
-[[winston-ref]]
-=== Reference
-
-[float]
-[[winston-ref-ecsFormat]]
-==== `ecsFormat([options])`
-
-* `options` +{type-object}+ The following options are supported:
-** `convertErr` +{type-boolean}+ Whether to convert a logged `err` field to ECS error fields. *Default:* `true`.
-** `convertReqRes` +{type-boolean}+ Whether to logged `req` and `res` HTTP request and response fields to ECS HTTP, User agent, and URL fields. *Default:* `false`.
-** `apmIntegration` +{type-boolean}+ Whether to enable APM agent integration. *Default:* `true`.
-** `serviceName` +{type-string}+ A "service.name" value. If specified this overrides any value from an active APM agent.
-** `serviceVersion` +{type-string}+ A "service.version" value. If specified this overrides any value from an active APM agent.
-** `serviceEnvironment` +{type-string}+ A "service.environment" value. If specified this overrides any value from an active APM agent.
-** `serviceNodeName` +{type-string}+ A "service.node.name" value. If specified this overrides any value from an active APM agent.
-** `eventDataset` +{type-string}+ A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
-
-Create a formatter for winston that emits in ECS Logging format. This is a
-single format that handles both <> and <>.
-The following two are equivalent:
-
-[source,js]
-----
-const { ecsFormat, ecsFields, ecsStringify } = require('@elastic/ecs-winston-format');
-const winston = require('winston');
-
-const logger = winston.createLogger({
- format: ecsFormat(/* options */),
- // ...
-});
-
-const logger = winston.createLogger({
- format: winston.format.combine(
- ecsFields(/* options */),
- ecsStringify()
- ),
- // ...
-});
-----
-
-[float]
-[[winston-ref-ecsFields]]
-==== `ecsFields([options])`
-
-* `options` +{type-object}+ The following options are supported:
-** `convertErr` +{type-boolean}+ Whether to convert a logged `err` field to ECS error fields. *Default:* `true`.
-** `convertReqRes` +{type-boolean}+ Whether to logged `req` and `res` HTTP request and response fields to ECS HTTP, User agent, and URL fields. *Default:* `false`.
-** `apmIntegration` +{type-boolean}+ Whether to enable APM agent integration. *Default:* `true`.
-** `serviceName` +{type-string}+ A "service.name" value. If specified this overrides any value from an active APM agent.
-** `serviceVersion` +{type-string}+ A "service.version" value. If specified this overrides any value from an active APM agent.
-** `serviceEnvironment` +{type-string}+ A "service.environment" value. If specified this overrides any value from an active APM agent.
-** `serviceNodeName` +{type-string}+ A "service.node.name" value. If specified this overrides any value from an active APM agent.
-** `eventDataset` +{type-string}+ A "event.dataset" value. If specified this overrides the default of using `${serviceVersion}`.
-
-Create a formatter for winston that converts fields on the log record info
-object to ECS Logging format.
-
-[float]
-[[winston-ref-ecsStringify]]
-==== `ecsStringify([options])`
-
-Create a formatter for winston that stringifies/serializes the log record to
-JSON.
-
-This is similar to `logform.json()`. They both use the `safe-stable-stringify`
-package to produce the JSON. Some differences:
-
-* This stringifier skips serializing the `level` field, because it is not
- an ECS field.
-* Winston provides a `replacer` that converts bigints to strings The argument
- *for* doing so is that a *JavaScript* JSON parser looses precision when
- parsing a bigint. The argument against is that a BigInt changes type to a
- string rather than a number. For now this stringifier does not convert
- BitInts to strings.