@@ -912,7 +912,7 @@ static int cb_azure_kusto_init(struct flb_output_instance *ins, struct flb_confi
912912
913913 ctx -> timer_created = FLB_FALSE ;
914914 ctx -> timer_ms = (int ) (ctx -> upload_timeout / 6 ) * 1000 ;
915- flb_plg_info (ctx -> ins , "Using upload size %lu bytes" , ctx -> file_size );
915+ flb_plg_debug (ctx -> ins , "Using upload size %lu bytes" , ctx -> file_size );
916916 }
917917
918918 flb_output_set_context (ins , ctx );
@@ -943,6 +943,63 @@ static int cb_azure_kusto_init(struct flb_output_instance *ins, struct flb_confi
943943 return -1 ;
944944 }
945945
946+ /*
947+ * Create upstream context for Kusto Cluster endpoint (for streaming ingestion)
948+ * Convert ingestion endpoint to cluster endpoint by removing "ingest-" prefix
949+ */
950+ if (ctx -> streaming_ingestion_enabled == FLB_TRUE ) {
951+ flb_sds_t cluster_endpoint = NULL ;
952+ const char * prefix = "ingest-" ;
953+ const char * schema_end = strstr (ctx -> ingestion_endpoint , "://" );
954+ const char * hostname_start = schema_end ? schema_end + 3 : ctx -> ingestion_endpoint ;
955+
956+ /* Check if hostname starts with "ingest-" prefix */
957+ if (strncmp (hostname_start , prefix , strlen (prefix )) == 0 ) {
958+ /* Create cluster endpoint by removing "ingest-" prefix from hostname */
959+ cluster_endpoint = flb_sds_create (ctx -> ingestion_endpoint );
960+ if (!cluster_endpoint ) {
961+ flb_plg_error (ctx -> ins , "failed to create cluster endpoint string" );
962+ flb_upstream_destroy (ctx -> u );
963+ ctx -> u = NULL ;
964+ return -1 ;
965+ }
966+
967+ /* Find the position in our copy and remove the prefix */
968+ char * copy_hostname = strstr (cluster_endpoint , "://" );
969+ if (copy_hostname ) {
970+ copy_hostname += 3 ;
971+ /* Verify the prefix is still at the expected position */
972+ if (strncmp (copy_hostname , prefix , strlen (prefix )) == 0 ) {
973+ memmove (copy_hostname , copy_hostname + strlen (prefix ),
974+ strlen (copy_hostname + strlen (prefix )) + 1 );
975+ flb_sds_len_set (cluster_endpoint , flb_sds_len (cluster_endpoint ) - strlen (prefix ));
976+ }
977+ }
978+
979+ /* Create upstream connection to cluster endpoint */
980+ ctx -> u_cluster = flb_upstream_create_url (config , cluster_endpoint , io_flags , ins -> tls );
981+ if (!ctx -> u_cluster ) {
982+ flb_plg_error (ctx -> ins , "cluster upstream creation failed for endpoint: %s" , cluster_endpoint );
983+ flb_sds_destroy (cluster_endpoint );
984+ flb_upstream_destroy (ctx -> u );
985+ ctx -> u = NULL ;
986+ return -1 ;
987+ }
988+
989+ flb_sds_destroy (cluster_endpoint );
990+ } else {
991+ flb_plg_warn (ctx -> ins , "ingestion endpoint hostname does not start with 'ingest-' prefix, using as cluster endpoint" );
992+ /* Use ingestion endpoint directly as cluster endpoint */
993+ ctx -> u_cluster = flb_upstream_create_url (config , ctx -> ingestion_endpoint , io_flags , ins -> tls );
994+ if (!ctx -> u_cluster ) {
995+ flb_plg_error (ctx -> ins , "cluster upstream creation failed" );
996+ flb_upstream_destroy (ctx -> u );
997+ ctx -> u = NULL ;
998+ return -1 ;
999+ }
1000+ }
1001+ }
1002+
9461003 flb_plg_debug (ctx -> ins , "async flag is %d" , flb_stream_is_async (& ctx -> u -> base ));
9471004
9481005 /* Create oauth2 context */
@@ -1396,22 +1453,64 @@ static void cb_azure_kusto_flush(struct flb_event_chunk *event_chunk,
13961453 }
13971454 flb_plg_trace (ctx -> ins , "payload size after compression %zu" , final_payload_size );
13981455
1399- /* Load or refresh ingestion resources */
1400- ret = azure_kusto_load_ingestion_resources (ctx , config );
1401- flb_plg_trace (ctx -> ins , "load_ingestion_resources: ret=%d" , ret );
1402- if (ret != 0 ) {
1403- flb_plg_error (ctx -> ins , "cannot load ingestion resources" );
1404- ret = FLB_RETRY ;
1405- goto error ;
1406- }
1456+ /* Check if streaming ingestion is enabled */
1457+ if (ctx -> streaming_ingestion_enabled == FLB_TRUE ) {
1458+ flb_plg_debug (ctx -> ins , "[FLUSH_STREAMING] Streaming ingestion mode enabled for tag: %s" , event_chunk -> tag );
1459+
1460+ /*
1461+ * Azure Kusto streaming ingestion has TWO limits:
1462+ * 1. Uncompressed data: 4MB limit
1463+ * 2. Compressed data: 1MB limit
1464+ * We need to check both limits
1465+ */
1466+
1467+ /* Check uncompressed data limit (4MB) */
1468+ if (json_size > 4194304 ) { /* 4MB = 4 * 1024 * 1024 */
1469+ flb_plg_error (ctx -> ins , "[FLUSH_STREAMING] ERROR: Uncompressed payload size %zu bytes exceeds 4MB limit for streaming ingestion" , json_size );
1470+ ret = FLB_ERROR ;
1471+ goto error ;
1472+ }
1473+
1474+ /* Check compressed data limit (1MB) if compression is enabled */
1475+ if (ctx -> compression_enabled == FLB_TRUE && final_payload_size > 1048576 ) { /* 1MB = 1024 * 1024 */
1476+ flb_plg_error (ctx -> ins , "[FLUSH_STREAMING] ERROR: Compressed payload size %zu bytes exceeds 1MB limit for streaming ingestion (uncompressed: %zu bytes)" , final_payload_size , json_size );
1477+ ret = FLB_ERROR ;
1478+ goto error ;
1479+ }
1480+
1481+ flb_plg_debug (ctx -> ins , "[FLUSH_STREAMING] Payload size checks passed - uncompressed: %zu bytes, compressed: %zu bytes" ,
1482+ json_size , final_payload_size );
14071483
1408- /* Perform queued ingestion to Kusto */
1409- ret = azure_kusto_queued_ingestion (ctx , event_chunk -> tag , tag_len , final_payload , final_payload_size , NULL );
1410- flb_plg_trace (ctx -> ins , "after kusto queued ingestion %d" , ret );
1411- if (ret != 0 ) {
1412- flb_plg_error (ctx -> ins , "cannot perform queued ingestion" );
1413- ret = FLB_RETRY ;
1414- goto error ;
1484+ /* Perform streaming ingestion to Kusto */
1485+ flb_plg_debug (ctx -> ins , "[FLUSH_STREAMING] Initiating streaming ingestion to Kusto" );
1486+ ret = azure_kusto_streaming_ingestion (ctx , event_chunk -> tag , tag_len , final_payload , final_payload_size );
1487+
1488+ if (ret != 0 ) {
1489+ flb_plg_error (ctx -> ins , "[FLUSH_STREAMING] ERROR: Streaming ingestion failed, will retry" );
1490+ ret = FLB_RETRY ;
1491+ goto error ;
1492+ } else {
1493+ flb_plg_info (ctx -> ins , "[FLUSH_STREAMING] SUCCESS: Streaming ingestion completed successfully" );
1494+ }
1495+ } else {
1496+ flb_plg_debug (ctx -> ins , "[FLUSH_QUEUED] Using queued ingestion mode (streaming ingestion disabled)" );
1497+ /* Load or refresh ingestion resources for queued ingestion */
1498+ ret = azure_kusto_load_ingestion_resources (ctx , config );
1499+ flb_plg_trace (ctx -> ins , "load_ingestion_resources: ret=%d" , ret );
1500+ if (ret != 0 ) {
1501+ flb_plg_error (ctx -> ins , "cannot load ingestion resources" );
1502+ ret = FLB_RETRY ;
1503+ goto error ;
1504+ }
1505+
1506+ /* Perform queued ingestion to Kusto */
1507+ ret = azure_kusto_queued_ingestion (ctx , event_chunk -> tag , tag_len , final_payload , final_payload_size , NULL );
1508+ flb_plg_trace (ctx -> ins , "after kusto queued ingestion %d" , ret );
1509+ if (ret != 0 ) {
1510+ flb_plg_error (ctx -> ins , "cannot perform queued ingestion" );
1511+ ret = FLB_RETRY ;
1512+ goto error ;
1513+ }
14151514 }
14161515
14171516 ret = FLB_OK ;
@@ -1501,6 +1600,11 @@ static int cb_azure_kusto_exit(void *data, struct flb_config *config)
15011600 ctx -> u = NULL ;
15021601 }
15031602
1603+ if (ctx -> u_cluster ) {
1604+ flb_upstream_destroy (ctx -> u_cluster );
1605+ ctx -> u_cluster = NULL ;
1606+ }
1607+
15041608 pthread_mutex_destroy (& ctx -> resources_mutex );
15051609 pthread_mutex_destroy (& ctx -> token_mutex );
15061610 pthread_mutex_destroy (& ctx -> blob_mutex );
@@ -1565,6 +1669,11 @@ static struct flb_config_map config_map[] = {
15651669 offsetof(struct flb_azure_kusto , compression_enabled ),
15661670 "Enable HTTP payload compression (gzip)."
15671671 "The default is true." },
1672+ {FLB_CONFIG_MAP_BOOL , "streaming_ingestion_enabled" , "false" , 0 , FLB_TRUE ,
1673+ offsetof(struct flb_azure_kusto , streaming_ingestion_enabled ),
1674+ "Enable streaming ingestion. When enabled, data is sent directly to Kusto engine without using blob storage and queues. "
1675+ "Note: Streaming ingestion has a 4MB limit per request and doesn't support buffering."
1676+ "The default is false (uses queued ingestion)." },
15681677 {FLB_CONFIG_MAP_TIME , "ingestion_resources_refresh_interval" , FLB_AZURE_KUSTO_RESOURCES_LOAD_INTERVAL_SEC ,0 , FLB_TRUE ,
15691678 offsetof(struct flb_azure_kusto , ingestion_resources_refresh_interval ),
15701679 "Set the azure kusto ingestion resources refresh interval"
0 commit comments