Azure Log Integration
Kloudfuse ingests Azure logs by routing them through an Azure Event Hub and a custom Azure Function App that forwards records to the Kloudfuse HTTP ingestion endpoint.
For an overview of how this fits into the broader Azure integration architecture, see Azure Integration Architecture.
Overview
Azure resources emit two categories of logs:
-
Activity logs — Account-level events such as resource creation, deletion, policy changes, and service health notifications.
-
Resource logs — Service-specific logs emitted by individual resources (for example, Azure SQL query logs, App Service HTTP access logs, or Key Vault audit logs).
Both categories are routed through the same pipeline:
-
Azure Diagnostic Settings forward logs from each resource to an Azure Event Hub Namespace.
-
An Azure Function App with an Event Hub trigger reads each batch of events and forwards them to the Kloudfuse logs ingestion endpoint over HTTPS.
-
Logs appear in the Kloudfuse Log Analytics interface within seconds.
Common use cases:
-
Security monitoring — Detect sign-in anomalies, privilege escalation, and suspicious API calls from Azure Active Directory and resource audit logs.
-
Compliance reporting — Maintain a tamper-evident record of all configuration changes to Azure resources.
-
Incident investigation — Correlate Azure infrastructure changes with application errors using Kloudfuse’s unified log and metrics view.
Prerequisites
-
An Azure subscription with permission to create Event Hubs, Function Apps, and Diagnostic Settings.
-
The Kloudfuse ingestion endpoint URL and an API key. See Ingestion Authentication with API Key for instructions on generating an API key.
-
The Azure CLI (
az) installed and authenticated, or access to the Azure Portal. -
Node.js 18 or later (for the Function App runtime).
Step 1: Create an Event Hub Namespace and Hub
An Event Hub Namespace is the container for one or more Event Hubs. Create a dedicated namespace and hub for Kloudfuse log ingestion.
Create the Namespace
az eventhubs namespace create \
--name kloudfuse-logs \
--resource-group <resource-group> \
--location <region> \
--sku Standard
Create the Event Hub
az eventhubs eventhub create \
--name kloudfuse \
--namespace-name kloudfuse-logs \
--resource-group <resource-group> \
--partition-count 4 \
--message-retention 1
Create a Shared Access Policy
Create a policy that allows both sending (for Diagnostic Settings) and listening (for the Function App):
az eventhubs namespace authorization-rule create \
--name kloudfuse-policy \
--namespace-name kloudfuse-logs \
--resource-group <resource-group> \
--rights Listen Send
Retrieve the connection string — you will need this when configuring the Function App:
az eventhubs namespace authorization-rule keys list \
--name kloudfuse-policy \
--namespace-name kloudfuse-logs \
--resource-group <resource-group> \
--query primaryConnectionString \
--output tsv
Save the output as EVENT_HUB_CONNECTION_STRING.
Step 2: Create an Azure Function App
The Function App reads events from the Event Hub and forwards them to Kloudfuse.
Create the Function App
Create a storage account (required by Function Apps) and the Function App itself. Both names must be globally unique in Azure:
-
Storage account names are 3–24 lowercase alphanumeric characters (for example,
kloudfuselogs<suffix>). -
Function App names become a subdomain of
azurewebsites.net(for example,kloudfuse-logs-<suffix>).
az storage account create \
--name <storage-account-name> \
--resource-group <resource-group> \
--location <region> \
--sku Standard_LRS
az functionapp create \
--name <function-app-name> \
--resource-group <resource-group> \
--storage-account <storage-account-name> \
--consumption-plan-location <region> \
--runtime node \
--runtime-version 18 \
--functions-version 4 \
--os-type Linux
Configure Application Settings
Set the Event Hub connection string and the Kloudfuse credentials as application settings:
az functionapp config appsettings set \
--name <function-app-name> \
--resource-group <resource-group> \
--settings \
"EventHubConnection=<EVENT_HUB_CONNECTION_STRING>" \
"KF_API_KEY=<your-kloudfuse-api-key>" \
"KF_URL=<your-kloudfuse-hostname>"
Replace <your-kloudfuse-hostname> with the hostname of your Kloudfuse instance only — no https:// prefix and no trailing slash, for example kloudfuse.example.com.
The forwarder constructs the HTTPS connection internally using the hostname.
Deploy the Function Code
Create the function directory structure and deploy the forwarding function:
mkdir -p kloudfuse-azure-fn/kloudfuse-log-forwarder
cd kloudfuse-azure-fn
Create host.json:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}
Create kloudfuse-log-forwarder/function.json:
{
"bindings": [
{
"type": "eventHubTrigger",
"name": "eventHubMessages",
"direction": "in",
"eventHubName": "kloudfuse",
"connection": "EventHubConnection",
"cardinality": "many",
"dataType": ""
}
]
}
Create kloudfuse-log-forwarder/index.js:
const https = require("https");
const KF_API_KEY = process.env.KF_API_KEY || "";
const KF_URL = process.env.KF_URL || ""; // hostname only, e.g. kloudfuse.example.com
module.exports = async function (context, eventHubMessages) {
const messages = Array.isArray(eventHubMessages)
? eventHubMessages
: [eventHubMessages];
// Normalise each message to a parsed object, then wrap the batch in a JSON array.
const records = messages.map(m => (typeof m === "string" ? JSON.parse(m) : m));
const payload = JSON.stringify(records);
await new Promise((resolve, reject) => {
const req = https.request(
{
hostname: KF_URL,
port: 443,
path: "/api/v2/logs",
method: "POST",
headers: {
"Content-Type": "application/json",
"DD-API-KEY": KF_API_KEY,
"Content-Length": Buffer.byteLength(payload),
},
},
res => {
let body = "";
res.on("data", chunk => { body += chunk; });
res.on("end", () => {
context.log(`Kloudfuse response: ${res.statusCode}`);
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve();
} else {
reject(new Error(`Kloudfuse ingester returned ${res.statusCode}: ${body}`));
}
});
}
);
req.on("error", reject);
req.write(payload);
req.end();
});
};
Deploy the function:
cd kloudfuse-azure-fn
func azure functionapp publish <function-app-name>
The Kloudfuse customer scripts repository contains a maintained reference implementation of index.js.
|
Step 3: Configure Diagnostic Settings
Diagnostic Settings control which Azure resources send logs to the Event Hub.
Retrieve the Authorization Rule Resource ID
Both activity log and resource log diagnostic settings require the full Azure Resource ID of the Event Hub authorization rule. Retrieve it with:
az eventhubs namespace authorization-rule show \
--name kloudfuse-policy \
--namespace-name kloudfuse-logs \
--resource-group <resource-group> \
--query id \
--output tsv
Save the output as EVENT_HUB_AUTH_RULE_RESOURCE_ID.
It follows the form:
/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.EventHub/namespaces/kloudfuse-logs/authorizationRules/kloudfuse-policy
Activity Logs (Subscription-Level)
Activity logs cover all API operations performed within a subscription. Configure them once per subscription:
az monitor diagnostic-settings subscription create \
--name kloudfuse-activity-logs \
--event-hub-name kloudfuse \
--event-hub-auth-rule "$EVENT_HUB_AUTH_RULE_RESOURCE_ID" \
--logs '[
{"category": "Administrative", "enabled": true},
{"category": "Security", "enabled": true},
{"category": "ServiceHealth", "enabled": true},
{"category": "Alert", "enabled": true},
{"category": "Policy", "enabled": true}
]'
Resource Logs (Per-Resource)
Resource logs must be enabled individually for each Azure resource you want to monitor. The following example enables diagnostic settings for an Azure SQL database:
az monitor diagnostic-settings create \
--name kloudfuse \
--resource <resource-id> \
--event-hub-name kloudfuse \
--event-hub-rule "$EVENT_HUB_AUTH_RULE_RESOURCE_ID" \
--logs '[{"categoryGroup": "allLogs", "enabled": true}]' \
--metrics '[{"category": "AllMetrics", "enabled": false}]'
Replace <resource-id> with the Azure Resource ID of the resource to monitor.
Use --logs '[{"categoryGroup": "allLogs", "enabled": true}]' to capture all log categories.
| Each Azure service exposes different log categories. See the Azure Monitor resource log categories reference for a full list. |
Verify the Integration
Confirm the Function App is Processing Events
-
In the Azure Portal, open the
<function-app-name>Function App. -
Click Functions in the left menu and select the
kloudfuse-log-forwarderfunction. -
Click Monitor to view recent invocations. Each row corresponds to one batch of Event Hub messages. The Status column must show Success.
-
Click a recent invocation to view the log output. A line like
Kloudfuse response: 200confirms successful delivery.
Confirm Logs are Arriving in Kloudfuse
-
In the Kloudfuse UI, click the Logs tab and select Search from the drop-down.
-
Set the time picker to the last 15 minutes.
-
In the search bar, click Advanced Search and enter the following FuseQL query to confirm Azure logs are flowing:
source="azure"fuseqlAny result confirms that Azure log records are reaching Kloudfuse.
-
To filter by log type, use the
categoryfield (set by Azure Diagnostic Settings):category="Administrative"fuseql -
To find all Azure Active Directory sign-in events:
source="azure" and operationName="Sign-in activity"fuseql -
To count log volume by Azure resource type:
source="azure" | count by resourceTypefuseql
Troubleshooting
Function App Has No Invocations
The Function App is not receiving events from the Event Hub.
-
Confirm that Diagnostic Settings are configured and pointing to the correct Event Hub Namespace and hub name.
-
In the Azure Portal, open the Event Hub and click Process data > Explore. Verify that incoming message counts are non-zero after making an API call in your subscription.
-
Confirm the
EventHubConnectionapplication setting in the Function App contains the correct connection string, including theEntityPath=kloudfusesuffix. -
Check that the authorization rule has both Listen and Send rights.
Function Invocations Fail with HTTP 5xx
The Function App is delivering to Kloudfuse but receiving error responses.
-
In the Function App Monitor view, click a failed invocation and check the log for the HTTP status code returned by Kloudfuse.
-
401or403— theKF_API_KEYvalue is incorrect or expired. Regenerate the key in Kloudfuse under Administration > API Keys and update the app setting. -
404— theKF_URLis incorrect or includes anhttps://prefix. The setting must be the hostname only (for examplekloudfuse.example.com). Verify the hostname is correct and that the Kloudfuse ingestion endpoint (/api/v2/logs) is reachable from the Function App. -
5xx— the Kloudfuse ingester returned an error. Check the Kloudfuse pod logs:kubectl logs -n kfuse -l app=ingester.
Logs Appear in the Function App but Not in Kloudfuse
-
Confirm the
KF_URLapplication setting contains the hostname only (for examplekloudfuse.example.com) with nohttps://prefix and no trailing slash. -
Confirm that the Function App can reach the Kloudfuse endpoint. If the Function App runs inside a VNet with restricted egress, add an outbound rule allowing HTTPS to the Kloudfuse IP range.
-
Check whether the log payload is valid JSON. The function forwards raw Event Hub messages; if Azure emits malformed records, the ingester may reject them silently.
Event Hub Lag is Growing
The Function App is falling behind the Event Hub.
-
In the Azure Portal, open the Event Hub and view the Consumer Groups lag metric for the Function App consumer group.
-
If lag is consistently above zero, increase the Event Hub partition count:
az eventhubs eventhub update --partition-count 8 … -
Alternatively, increase the Function App plan to a Premium or Dedicated plan to allow more concurrent executions.