@@ -517,6 +517,147 @@ static int azure_kusto_enqueue_ingestion(struct flb_azure_kusto *ctx, flb_sds_t
517517 return ret ;
518518}
519519
520+ int azure_kusto_streaming_ingestion (struct flb_azure_kusto * ctx , flb_sds_t tag ,
521+ size_t tag_len , flb_sds_t payload , size_t payload_size )
522+ {
523+ int ret = -1 ;
524+ struct flb_connection * u_conn ;
525+ struct flb_http_client * c ;
526+ flb_sds_t uri = NULL ;
527+ flb_sds_t token = NULL ;
528+ size_t resp_size ;
529+ time_t now ;
530+ struct tm tm ;
531+ char tmp [64 ];
532+ int len ;
533+
534+ flb_plg_info (ctx -> ins , "[STREAMING_INGESTION] Starting for tag: %.*s, payload: %zu bytes, db: %s, table: %s, compression: %s" ,
535+ (int )tag_len , tag , payload_size , ctx -> database_name , ctx -> table_name , ctx -> compression_enabled ? "enabled" : "disabled" );
536+
537+ now = time (NULL );
538+ gmtime_r (& now , & tm );
539+ len = strftime (tmp , sizeof (tmp ) - 1 , "%a, %d %b %Y %H:%M:%S GMT" , & tm );
540+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Request timestamp: %s" , tmp );
541+
542+ /* Get upstream connection to the main Kusto cluster endpoint (for streaming ingestion) */
543+ if (!ctx -> u_cluster ) {
544+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] ERROR: cluster upstream not available - streaming ingestion requires cluster endpoint" );
545+ return -1 ;
546+ }
547+
548+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Getting upstream connection to cluster endpoint" );
549+ u_conn = flb_upstream_conn_get (ctx -> u_cluster );
550+ if (!u_conn ) {
551+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] ERROR: failed to get cluster upstream connection" );
552+ return -1 ;
553+ }
554+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Successfully obtained upstream connection" );
555+
556+ /* Get authentication token */
557+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Retrieving OAuth2 authentication token" );
558+ token = get_azure_kusto_token (ctx );
559+ if (!token ) {
560+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] ERROR: failed to retrieve OAuth2 token" );
561+ flb_upstream_conn_release (u_conn );
562+ return -1 ;
563+ }
564+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Successfully obtained OAuth2 token (length: %zu)" , flb_sds_len (token ));
565+
566+ /* Build the streaming ingestion URI */
567+ uri = flb_sds_create_size (256 );
568+ if (!uri ) {
569+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] ERROR: failed to create URI buffer" );
570+ flb_sds_destroy (token );
571+ flb_upstream_conn_release (u_conn );
572+ return -1 ;
573+ }
574+
575+ /* Create the streaming ingestion URI */
576+ if (ctx -> ingestion_mapping_reference ) {
577+ flb_sds_snprintf (& uri , flb_sds_alloc (uri ),
578+ "/v1/rest/ingest/%s/%s?streamFormat=MultiJSON&mappingName=%s" ,
579+ ctx -> database_name , ctx -> table_name , ctx -> ingestion_mapping_reference );
580+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Using ingestion mapping: %s" , ctx -> ingestion_mapping_reference );
581+ } else {
582+ flb_sds_snprintf (& uri , flb_sds_alloc (uri ),
583+ "/v1/rest/ingest/%s/%s?streamFormat=MultiJSON" ,
584+ ctx -> database_name , ctx -> table_name );
585+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] No ingestion mapping specified" );
586+ }
587+ flb_plg_info (ctx -> ins , "[STREAMING_INGESTION] Request URI: %s" , uri );
588+
589+ /* Create HTTP client for streaming ingestion */
590+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Creating HTTP client for POST request" );
591+ c = flb_http_client (u_conn , FLB_HTTP_POST , uri , payload , payload_size ,
592+ NULL , 0 , NULL , 0 );
593+
594+ if (c ) {
595+ /* Add required headers for streaming ingestion */
596+ flb_http_add_header (c , "User-Agent" , 10 , "Fluent-Bit" , 10 );
597+ flb_http_add_header (c , "Accept" , 6 , "application/json" , 16 );
598+ flb_http_add_header (c , "Authorization" , 13 , token , flb_sds_len (token ));
599+ flb_http_add_header (c , "x-ms-date" , 9 , tmp , len );
600+ flb_http_add_header (c , "x-ms-client-version" , 19 , FLB_VERSION_STR , strlen (FLB_VERSION_STR ));
601+ flb_http_add_header (c , "x-ms-app" , 8 , "Kusto.Fluent-Bit" , 16 );
602+ flb_http_add_header (c , "x-ms-user" , 9 , "Kusto.Fluent-Bit" , 16 );
603+
604+ /* Set Content-Type based on whether compression is enabled */
605+ if (ctx -> compression_enabled ) {
606+ flb_http_add_header (c , "Content-Type" , 12 , "application/json" , 16 );
607+ flb_http_add_header (c , "Content-Encoding" , 16 , "gzip" , 4 );
608+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Headers set for compressed payload" );
609+ } else {
610+ flb_http_add_header (c , "Content-Type" , 12 , "application/json; charset=utf-8" , 31 );
611+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Headers set for uncompressed payload" );
612+ }
613+
614+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Payload sample (first 200 chars): %.200s" , (char * )payload );
615+ flb_plg_info (ctx -> ins , "[STREAMING_INGESTION] Sending HTTP POST request to Kusto engine" );
616+
617+ /* Send the HTTP request */
618+ ret = flb_http_do (c , & resp_size );
619+
620+ flb_plg_info (ctx -> ins , "[STREAMING_INGESTION] HTTP request completed - http_do result: %d, HTTP Status: %i, Response size: %zu" , ret , c -> resp .status , resp_size );
621+
622+ if (ret == 0 ) {
623+ /* Check for successful HTTP status codes */
624+ if (c -> resp .status == 200 || c -> resp .status == 204 ) {
625+ ret = 0 ;
626+ flb_plg_info (ctx -> ins , "[STREAMING_INGESTION] SUCCESS: Data successfully ingested to Kusto (HTTP %d)" , c -> resp .status );
627+ } else {
628+ ret = -1 ;
629+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] ERROR: HTTP request failed with status: %i" , c -> resp .status );
630+
631+ if (c -> resp .payload_size > 0 ) {
632+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] Error response body (size: %zu): %.*s" ,
633+ c -> resp .payload_size , (int )c -> resp .payload_size , c -> resp .payload );
634+ }
635+ }
636+ } else {
637+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] ERROR: HTTP request failed at transport level (ret=%d)" , ret );
638+ }
639+
640+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Destroying HTTP client" );
641+ flb_http_client_destroy (c );
642+ } else {
643+ flb_plg_error (ctx -> ins , "[STREAMING_INGESTION] ERROR: failed to create HTTP client context" );
644+ }
645+
646+ /* Cleanup */
647+ flb_plg_debug (ctx -> ins , "[STREAMING_INGESTION] Cleaning up resources" );
648+ if (uri ) {
649+ flb_sds_destroy (uri );
650+ }
651+ if (token ) {
652+ flb_sds_destroy (token );
653+ }
654+ flb_upstream_conn_release (u_conn );
655+
656+ flb_plg_info (ctx -> ins , "[STREAMING_INGESTION] Streaming ingestion completed with result: %d" , ret );
657+ return ret ;
658+ }
659+
660+
520661/* Function to generate a random alphanumeric string */
521662void generate_random_string (char * str , size_t length )
522663{
@@ -658,4 +799,4 @@ int azure_kusto_queued_ingestion(struct flb_azure_kusto *ctx, flb_sds_t tag,
658799 }
659800
660801 return ret ;
661- }
802+ }
0 commit comments