All skills
secondsky avatar

/sap-btp-cloud-logging

@620a19a
by Eddiesecondsky/sap-skills456 stars
120

This skill provides comprehensive guidance for SAP Cloud Logging service on SAP BTP. Use when setting up Cloud Logging instances, configuring log ingestion from Cloud Foundry or Kyma runtimes, implementing OpenTelemetry observability, analyzing logs/metrics/traces in OpenSearch Dashboards, configuring SAML authentication, managing certificates, or troubleshooting ingestion issues. Covers service plans (dev/standard/large), all 4 instance creation methods (BTP Cockpit, CF CLI, BTP CLI, Service Operator), all 4 ingestion methods (Cloud Foundry, Kyma, OpenTelemetry, JSON API), and security best practices.

Use this Skill: https://skilld.dev/gh/secondsky/sap-skills/sap-btp-cloud-logging

This session only. Nothing lands on disk.

referencesjson-api-ingestion.md

≈3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

SAP Cloud Logging - JSON API Ingestion Reference

Source: https://github.com/SAP-docs/btp-cloud-logging/blob/main/docs/ingest-via-json-api-endpoint-3416f8f.md Last Updated: 2025-11-22


Table of Contents


Overview

SAP Cloud Logging supports document ingestion via JSON API endpoint as an alternative to OpenTelemetry format. The service uses mTLS authentication for secure data transmission.

Security Notice: Review SAP BTP Security Recommendation BTP-CLS-0003 before implementation.


Index Pattern

Data Type Index Pattern Regex Pattern
JSON API Logs logs-json-* logs-json-.*

Note: Use the regex pattern logs-json-.* when creating custom index patterns in OpenSearch Dashboards.


Endpoint Details

Credentials from Service Key

Credential Description
ingest-endpoint JSON API endpoint URL
ingest-mtls-cert Client certificate (PEM)
ingest-mtls-key Client private key (PKCS#8)
server-ca Server CA certificate (PEM)

Certificate Validity

  • Default: 90 days
  • Configurable: 1-180 days via certValidityDays
  • Rotation: Create new binding before expiration

API Specification

Single Document Ingestion

curl -X PUT "https://<ingest-endpoint>/v1/ingest" \
  --cert client.crt \
  --key client.key \
  --cacert server-ca.crt \
  -H "Content-Type: application/json" \
  -d '{
    "msg": "Application started successfully",
    "date": "2025-01-15T10:30:00Z",
    "level": "INFO",
    "app": "my-application"
  }'

Batch Document Ingestion

curl -X PUT "https://<ingest-endpoint>/v1/ingest" \
  --cert client.crt \
  --key client.key \
  --cacert server-ca.crt \
  -H "Content-Type: application/json" \
  -d '[
    {"msg": "Request received", "date": "2025-01-15T10:30:00Z", "level": "INFO"},
    {"msg": "Processing started", "date": "2025-01-15T10:30:01Z", "level": "DEBUG"},
    {"msg": "Processing completed", "date": "2025-01-15T10:30:05Z", "level": "INFO"}
  ]'

Compressed Payload

# gzip compression
curl -X PUT "https://<ingest-endpoint>/v1/ingest" \
  --cert client.crt \
  --key client.key \
  --cacert server-ca.crt \
  -H "Content-Type: application/json" \
  -H "Content-Encoding: gzip" \
  --data-binary @logs.json.gz

# deflate compression
curl -X PUT "https://<ingest-endpoint>/v1/ingest" \
  --cert client.crt \
  --key client.key \
  --cacert server-ca.crt \
  -H "Content-Type: application/json" \
  -H "Content-Encoding: deflate" \
  --data-binary @logs.json.deflate

Field Specifications

Required Fields

Field Type Default Description
date string/number Current time Timestamp
msg string - Log message content

Date Format Options

Format Example Type
Epoch Unix Timestamp 1705315800 number
ISO 8601 "2025-01-15T10:30:00Z" string
ISO 8601 with timezone "2025-01-15T10:30:00+01:00" string

Additional Fields

  • Supports up to 1000 custom fields per document
  • Type consistency required: Field types must be consistent across documents
  • Field names should follow lowercase naming convention

Example with custom fields:

{
  "msg": "User login successful",
  "date": "2025-01-15T10:30:00Z",
  "level": "INFO",
  "user_id": "user123",
  "ip_address": "192.168.1.100",
  "session_id": "sess_abc123",
  "response_time_ms": 45,
  "success": true
}

Response Codes

Code Description
200 Success
400 Invalid JSON payload (error description in body)
401 Authentication failed
413 Payload too large
500 Server error

Fluent Bit Configuration

Fluent Bit is recommended for production log shipping.

Basic Configuration

[SERVICE]
    Flush         5
    Daemon        Off
    Log_Level     info

[INPUT]
    Name          tail
    Path          /var/log/app/*.log
    Tag           app.logs

[OUTPUT]
    Name          http
    Match         *
    Host          <ingest-endpoint-host>
    Port          443
    URI           /v1/ingest
    Format        json
    Json_date_key date
    Json_date_format iso8601
    tls           On
    tls.verify    On
    tls.ca_file   /certs/server-ca.crt
    tls.crt_file  /certs/client.crt
    tls.key_file  /certs/client.key
    Compress      gzip
    Retry_Limit   3

Kubernetes DaemonSet

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
spec:
  selector:
    matchLabels:
      app: fluent-bit
  template:
    metadata:
      labels:
        app: fluent-bit
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:latest
        volumeMounts:
        - name: config
          mountPath: /fluent-bit/etc/
        - name: certs
          mountPath: /certs
          readOnly: true
        - name: varlog
          mountPath: /var/log
          readOnly: true
      volumes:
      - name: config
        configMap:
          name: fluent-bit-config
      - name: certs
        secret:
          secretName: cls-json-credentials
      - name: varlog
        hostPath:
          path: /var/log

ConfigMap for Fluent Bit

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: logging
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         5
        Daemon        Off
        Log_Level     info
        Parsers_File  parsers.conf

    [INPUT]
        Name              tail
        Path              /var/log/containers/*.log
        Parser            docker
        Tag               kube.*
        Refresh_Interval  5
        Mem_Buf_Limit     50MB
        Skip_Long_Lines   On

    [FILTER]
        Name                kubernetes
        Match               kube.*
        Kube_URL            https://kubernetes.default.svc:443
        Kube_CA_File        /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        Kube_Token_File     /var/run/secrets/kubernetes.io/serviceaccount/token
        Merge_Log           On
        K8S-Logging.Parser  On
        K8S-Logging.Exclude On

    [OUTPUT]
        Name          http
        Match         *
        Host          <ingest-endpoint-host>
        Port          443
        URI           /v1/ingest
        Format        json
        Json_date_key date
        Json_date_format iso8601
        tls           On
        tls.verify    On
        tls.ca_file   /certs/server-ca.crt
        tls.crt_file  /certs/client.crt
        tls.key_file  /certs/client.key
        Compress      gzip
        Retry_Limit   3

  parsers.conf: |
    [PARSER]
        Name        docker
        Format      json
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L
        Time_Keep   On

Logstash Configuration

Alternative to Fluent Bit for existing Logstash deployments.

output {
  http {
    url => "https://<ingest-endpoint>/v1/ingest"
    http_method => "put"
    format => "json"
    content_type => "application/json"
    client_cert => "/certs/client.crt"
    client_key => "/certs/client.key"
    cacert => "/certs/server-ca.crt"

    # Map fields
    mapping => {
      "msg" => "%{message}"
      "date" => "%{@timestamp}"
    }
  }
}

Application Integration Examples

Python

import requests
import json
from datetime import datetime

def send_logs_to_cls(logs, endpoint, cert_path, key_path, ca_path):
    """Send logs to SAP Cloud Logging JSON API."""

    # Add timestamp if not present
    for log in logs:
        if 'date' not in log:
            log['date'] = datetime.utcnow().isoformat() + 'Z'
        if 'msg' not in log:
            log['msg'] = '-'

    response = requests.put(
        f"https://{endpoint}/v1/ingest",
        json=logs,
        cert=(cert_path, key_path),
        verify=ca_path,
        headers={'Content-Type': 'application/json'}
    )

    return response.status_code == 200

# Usage
logs = [
    {"msg": "Application started", "level": "INFO"},
    {"msg": "Connected to database", "level": "INFO"}
]
send_logs_to_cls(logs, "ingest.cls.example.com", "client.crt", "client.key", "server-ca.crt")

Node.js

const https = require('https');
const fs = require('fs');

function sendLogsToCLS(logs, endpoint, certPath, keyPath, caPath) {
  // Add timestamp if not present
  const enrichedLogs = logs.map(log => ({
    ...log,
    date: log.date || new Date().toISOString(),
    msg: log.msg || '-'
  }));

  const options = {
    hostname: endpoint,
    port: 443,
    path: '/v1/ingest',
    method: 'PUT',
    cert: fs.readFileSync(certPath),
    key: fs.readFileSync(keyPath),
    ca: fs.readFileSync(caPath),
    headers: {
      'Content-Type': 'application/json'
    }
  };

  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      resolve(res.statusCode === 200);
    });
    req.on('error', reject);
    req.write(JSON.stringify(enrichedLogs));
    req.end();
  });
}

// Usage
const logs = [
  { msg: 'Application started', level: 'INFO' },
  { msg: 'Connected to database', level: 'INFO' }
];
sendLogsToCLS(logs, 'ingest.cls.example.com', 'client.crt', 'client.key', 'server-ca.crt');

Troubleshooting

HTTP 400 Bad Request

  1. Validate JSON syntax
  2. Check field type consistency
  3. Verify date format is valid

HTTP 401 Unauthorized

  1. Verify certificate hasn't expired
  2. Check certificate matches service key
  3. Ensure CA certificate is included

Data Not Appearing in Dashboards

  1. Check index pattern: logs-json-*
  2. Verify timestamp field is named date
  3. Allow time for indexing (1-2 minutes)

Performance Issues

  1. Use batch uploads instead of single documents
  2. Enable gzip compression
  3. Increase Fluent Bit buffer size
  4. Reduce flush interval if losing logs

Documentation Links

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides operational guidance for SAP BTP Cloud Logging, including setup instructions, CLI commands, and telemetry configuration. It identifies an inherent surface for indirect prompt injection through the ingestion of external log data.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    8/10 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 620a19a. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago
Other metadata
metadata
{
  "maintainer": "Eduard Jiglau",
  "maintainer_email": "hello@sap-ai-skills.com",
  "website": "https://sap-ai-skills.com",
  "version": "2.4.1",
  "last_verified": "2025-11-27",
  "source_documentation": "https://github.com/SAP-docs/btp-cloud-logging",
  "sap_help_portal": "https://help.sap.com/docs/cloud-logging"
}

README badge

README badge for secondsky/sap-skills/sap-btp-cloud-logging