@@ -514,6 +514,348 @@ mod tests {
514514 assert ! ( !obj. contains_key( "timestamp" ) ) ;
515515 }
516516
517+ #[ test]
518+ fn test_boomi_container_log ( ) {
519+ let processor = EventProcessor :: new ( FORMATS_JSON ) ;
520+ let schema = processor
521+ . schema_definitions
522+ . get ( "boomi_container_log" )
523+ . unwrap ( ) ;
524+
525+ let test_logs = vec ! [
526+ // INFO - process scheduling
527+ (
528+ "Dec 21, 2023 12:00:00 AM UTC INFO [com.boomi.process.ProcessListener%ProcessRunner run] Skipping execution of process Main:PDM9000ToMarxLogicPartsDataProc [alstomtransport-BFJG449], schedules are paused." ,
529+ "Dec 21, 2023 12:00:00 AM UTC" ,
530+ "INFO" ,
531+ "com.boomi.process.ProcessListener%ProcessRunner run" ,
532+ "Skipping execution of process Main:PDM9000ToMarxLogicPartsDataProc [alstomtransport-BFJG449], schedules are paused." ,
533+ ) ,
534+ // INFO - connector operation
535+ (
536+ "Jan 15, 2024 01:03:36 AM UTC INFO [com.boomi.connector.databaseconnector.get.StandardGetOperation] Values appended for prepared statement. Parameters: 5." ,
537+ "Jan 15, 2024 01:03:36 AM UTC" ,
538+ "INFO" ,
539+ "com.boomi.connector.databaseconnector.get.StandardGetOperation" ,
540+ "Values appended for prepared statement. Parameters: 5." ,
541+ ) ,
542+ // WARNING - notify shape
543+ (
544+ "Dec 21, 2023 12:00:14 AM UTC WARNING [com.boomi.process.shape.NotifyShape appendMessage] Notify Shape message size exceeded." ,
545+ "Dec 21, 2023 12:00:14 AM UTC" ,
546+ "WARNING" ,
547+ "com.boomi.process.shape.NotifyShape appendMessage" ,
548+ "Notify Shape message size exceeded." ,
549+ ) ,
550+ // SEVERE - OOM error
551+ (
552+ "Jan 15, 2024 12:00:00 AM UTC SEVERE [com.boomi.container.ContainerController start] Failed to start container [acme-corp-PROD01]. java.lang.OutOfMemoryError: Java heap space" ,
553+ "Jan 15, 2024 12:00:00 AM UTC" ,
554+ "SEVERE" ,
555+ "com.boomi.container.ContainerController start" ,
556+ "Failed to start container [acme-corp-PROD01]. java.lang.OutOfMemoryError: Java heap space" ,
557+ ) ,
558+ // INFO - startup with version details
559+ (
560+ "Jan 15, 2024 12:00:00 AM UTC INFO [com.boomi.container.ContainerController start] Atom [acme-corp-PROD01] starting. Version: 24.01.2. JVM: OpenJDK 11.0.21. OS: Linux 5.15.0-91-generic amd64." ,
561+ "Jan 15, 2024 12:00:00 AM UTC" ,
562+ "INFO" ,
563+ "com.boomi.container.ContainerController start" ,
564+ "Atom [acme-corp-PROD01] starting. Version: 24.01.2. JVM: OpenJDK 11.0.21. OS: Linux 5.15.0-91-generic amd64." ,
565+ ) ,
566+ // WARNING - TryCatch with special characters in body
567+ (
568+ "Jan 15, 2024 01:08:37 AM UTC WARNING [com.boomi.process.shape.TryCatchShape handleError] Caught exception in Try/Catch for process Main:ErrorNotificationHandler: java.lang.NumberFormatException: For input string: \" N/A\" " ,
569+ "Jan 15, 2024 01:08:37 AM UTC" ,
570+ "WARNING" ,
571+ "com.boomi.process.shape.TryCatchShape handleError" ,
572+ "Caught exception in Try/Catch for process Main:ErrorNotificationHandler: java.lang.NumberFormatException: For input string: \" N/A\" " ,
573+ ) ,
574+ ] ;
575+
576+ for ( i, ( log_text, expected_ts, expected_level, expected_logger, expected_body) ) in
577+ test_logs. iter ( ) . enumerate ( )
578+ {
579+ let mut obj = Map :: new ( ) ;
580+ let log_field = "raw_log" ;
581+ obj. insert ( log_field. to_string ( ) , Value :: String ( log_text. to_string ( ) ) ) ;
582+
583+ let result = schema. check_or_extract ( & mut obj, Some ( log_field) ) ;
584+ assert ! (
585+ result. is_some( ) ,
586+ "Failed to extract fields from boomi container log {}: {}" ,
587+ i + 1 ,
588+ log_text
589+ ) ;
590+
591+ assert_eq ! (
592+ obj. get( "timestamp" ) . unwrap( ) . as_str( ) . unwrap( ) ,
593+ * expected_ts,
594+ "Timestamp mismatch for log {}" ,
595+ i + 1
596+ ) ;
597+ assert_eq ! (
598+ obj. get( "level" ) . unwrap( ) . as_str( ) . unwrap( ) ,
599+ * expected_level,
600+ "Level mismatch for log {}" ,
601+ i + 1
602+ ) ;
603+ assert_eq ! (
604+ obj. get( "logger" ) . unwrap( ) . as_str( ) . unwrap( ) ,
605+ * expected_logger,
606+ "Logger mismatch for log {}" ,
607+ i + 1
608+ ) ;
609+ assert_eq ! (
610+ obj. get( "body" ) . unwrap( ) . as_str( ) . unwrap( ) ,
611+ * expected_body,
612+ "Body mismatch for log {}" ,
613+ i + 1
614+ ) ;
615+ }
616+ }
617+
618+ #[ test]
619+ fn test_boomi_container_log_no_match ( ) {
620+ let processor = EventProcessor :: new ( FORMATS_JSON ) ;
621+ let schema = processor
622+ . schema_definitions
623+ . get ( "boomi_container_log" )
624+ . unwrap ( ) ;
625+
626+ let invalid_logs = vec ! [
627+ "This is not a Boomi log line" ,
628+ "2024-01-15T01:03:36Z INFO [some.logger] Not Boomi timestamp format" ,
629+ "Jan 15, 2024 01:03:36 AM UTC DEBUG [com.boomi.test] Debug is not a valid Boomi level" ,
630+ ] ;
631+
632+ for ( i, log_text) in invalid_logs. iter ( ) . enumerate ( ) {
633+ let mut obj = Map :: new ( ) ;
634+ obj. insert ( "raw_log" . to_string ( ) , Value :: String ( log_text. to_string ( ) ) ) ;
635+
636+ let result = schema. check_or_extract ( & mut obj, Some ( "raw_log" ) ) ;
637+ assert ! (
638+ result. is_none( ) ,
639+ "Should not match invalid log {}: {}" ,
640+ i + 1 ,
641+ log_text
642+ ) ;
643+ }
644+ }
645+
646+ #[ test]
647+ fn test_boomi_webserver_log ( ) {
648+ let processor = EventProcessor :: new ( FORMATS_JSON ) ;
649+ let schema = processor
650+ . schema_definitions
651+ . get ( "boomi_webserver_log" )
652+ . unwrap ( ) ;
653+
654+ let test_logs = vec ! [
655+ // Standard POST with 200
656+ (
657+ r#"192.168.1.200 - - [15/Jan/2024:00:00:55 +0000] "POST /ws/rest/v1/invoices HTTP/1.1" 200 481480 "-" "python-requests/2.31.0" 10530ms"# ,
658+ "192.168.1.200" ,
659+ "POST" ,
660+ "/ws/rest/v1/invoices" ,
661+ "HTTP/1.1" ,
662+ "200" ,
663+ "481480" ,
664+ "python-requests/2.31.0" ,
665+ "10530" ,
666+ ) ,
667+ // GET with 400 error
668+ (
669+ r#"10.0.1.45 - - [15/Jan/2024:00:04:36 +0000] "GET /ws/rest/v1/edi/850 HTTP/1.1" 400 840 "-" "curl/8.1.2" 14225ms"# ,
670+ "10.0.1.45" ,
671+ "GET" ,
672+ "/ws/rest/v1/edi/850" ,
673+ "HTTP/1.1" ,
674+ "400" ,
675+ "840" ,
676+ "curl/8.1.2" ,
677+ "14225" ,
678+ ) ,
679+ // GET with 500 error
680+ (
681+ r#"172.16.0.88 - - [15/Jan/2024:00:59:04 +0000] "GET /ws/rest/v1/edi/850 HTTP/1.1" 500 369 "-" "Apache-HttpClient/4.5.13" 4620ms"# ,
682+ "172.16.0.88" ,
683+ "GET" ,
684+ "/ws/rest/v1/edi/850" ,
685+ "HTTP/1.1" ,
686+ "500" ,
687+ "369" ,
688+ "Apache-HttpClient/4.5.13" ,
689+ "4620" ,
690+ ) ,
691+ // PUT with SAP user agent
692+ (
693+ r#"10.0.1.102 - - [15/Jan/2024:00:28:40 +0000] "PUT /ws/rest/v1/products HTTP/1.1" 200 77663 "-" "SAP-NetWeaver/7.50" 1003ms"# ,
694+ "10.0.1.102" ,
695+ "PUT" ,
696+ "/ws/rest/v1/products" ,
697+ "HTTP/1.1" ,
698+ "200" ,
699+ "77663" ,
700+ "SAP-NetWeaver/7.50" ,
701+ "1003" ,
702+ ) ,
703+ // DELETE with Salesforce user agent
704+ (
705+ r#"172.16.0.15 - - [15/Jan/2024:01:08:39 +0000] "DELETE /ws/rest/v1/orders HTTP/1.1" 400 1302 "-" "Salesforce/1.0" 24919ms"# ,
706+ "172.16.0.15" ,
707+ "DELETE" ,
708+ "/ws/rest/v1/orders" ,
709+ "HTTP/1.1" ,
710+ "400" ,
711+ "1302" ,
712+ "Salesforce/1.0" ,
713+ "24919" ,
714+ ) ,
715+ ] ;
716+
717+ for (
718+ i,
719+ (
720+ log_text,
721+ expected_ip,
722+ expected_method,
723+ expected_path,
724+ expected_version,
725+ expected_status,
726+ expected_bytes,
727+ expected_ua,
728+ expected_duration,
729+ ) ,
730+ ) in test_logs. iter ( ) . enumerate ( )
731+ {
732+ let mut obj = Map :: new ( ) ;
733+ let log_field = "raw_log" ;
734+ obj. insert ( log_field. to_string ( ) , Value :: String ( log_text. to_string ( ) ) ) ;
735+
736+ let result = schema. check_or_extract ( & mut obj, Some ( log_field) ) ;
737+ assert ! (
738+ result. is_some( ) ,
739+ "Failed to extract fields from boomi webserver log {}: {}" ,
740+ i + 1 ,
741+ log_text
742+ ) ;
743+
744+ assert_eq ! (
745+ obj. get( "c_ip" ) . unwrap( ) . as_str( ) . unwrap( ) ,
746+ * expected_ip,
747+ "IP mismatch for log {}" ,
748+ i + 1
749+ ) ;
750+ assert_eq ! (
751+ obj. get( "cs_method" ) . unwrap( ) . as_str( ) . unwrap( ) ,
752+ * expected_method,
753+ "Method mismatch for log {}" ,
754+ i + 1
755+ ) ;
756+ assert_eq ! (
757+ obj. get( "cs_uri_stem" ) . unwrap( ) . as_str( ) . unwrap( ) ,
758+ * expected_path,
759+ "Path mismatch for log {}" ,
760+ i + 1
761+ ) ;
762+ assert_eq ! (
763+ obj. get( "cs_version" ) . unwrap( ) . as_str( ) . unwrap( ) ,
764+ * expected_version,
765+ "HTTP version mismatch for log {}" ,
766+ i + 1
767+ ) ;
768+ assert_eq ! (
769+ obj. get( "sc_status" ) . unwrap( ) . as_str( ) . unwrap( ) ,
770+ * expected_status,
771+ "Status mismatch for log {}" ,
772+ i + 1
773+ ) ;
774+ assert_eq ! (
775+ obj. get( "sc_bytes" ) . unwrap( ) . as_str( ) . unwrap( ) ,
776+ * expected_bytes,
777+ "Bytes mismatch for log {}" ,
778+ i + 1
779+ ) ;
780+ assert_eq ! (
781+ obj. get( "cs_user_agent" ) . unwrap( ) . as_str( ) . unwrap( ) ,
782+ * expected_ua,
783+ "User agent mismatch for log {}" ,
784+ i + 1
785+ ) ;
786+ assert_eq ! (
787+ obj. get( "duration" ) . unwrap( ) . as_str( ) . unwrap( ) ,
788+ * expected_duration,
789+ "Duration mismatch for log {}" ,
790+ i + 1
791+ ) ;
792+ }
793+ }
794+
795+ #[ test]
796+ fn test_boomi_webserver_log_no_match ( ) {
797+ let processor = EventProcessor :: new ( FORMATS_JSON ) ;
798+ let schema = processor
799+ . schema_definitions
800+ . get ( "boomi_webserver_log" )
801+ . unwrap ( ) ;
802+
803+ let invalid_logs = vec ! [
804+ "This is not a web server log" ,
805+ // Missing duration suffix
806+ r#"192.168.1.200 - - [15/Jan/2024:00:00:55 +0000] "POST /ws/rest/v1/invoices HTTP/1.1" 200 481480 "-" "python-requests/2.31.0""# ,
807+ ] ;
808+
809+ for ( i, log_text) in invalid_logs. iter ( ) . enumerate ( ) {
810+ let mut obj = Map :: new ( ) ;
811+ obj. insert ( "raw_log" . to_string ( ) , Value :: String ( log_text. to_string ( ) ) ) ;
812+
813+ let result = schema. check_or_extract ( & mut obj, Some ( "raw_log" ) ) ;
814+ assert ! (
815+ result. is_none( ) ,
816+ "Should not match invalid log {}: {}" ,
817+ i + 1 ,
818+ log_text
819+ ) ;
820+ }
821+ }
822+
823+ #[ test]
824+ fn test_boomi_container_log_fields_already_exist ( ) {
825+ let processor = EventProcessor :: new ( FORMATS_JSON ) ;
826+ let schema = processor
827+ . schema_definitions
828+ . get ( "boomi_container_log" )
829+ . unwrap ( ) ;
830+
831+ let mut obj = Map :: new ( ) ;
832+ obj. insert (
833+ "timestamp" . to_string ( ) ,
834+ Value :: String ( "Jan 15, 2024 01:03:36 AM UTC" . to_string ( ) ) ,
835+ ) ;
836+ obj. insert ( "level" . to_string ( ) , Value :: String ( "INFO" . to_string ( ) ) ) ;
837+ obj. insert (
838+ "logger" . to_string ( ) ,
839+ Value :: String ( "com.boomi.container.ContainerController" . to_string ( ) ) ,
840+ ) ;
841+ obj. insert (
842+ "body" . to_string ( ) ,
843+ Value :: String ( "Atom started successfully." . to_string ( ) ) ,
844+ ) ;
845+
846+ let result = schema. check_or_extract ( & mut obj, None ) ;
847+ assert ! (
848+ result. is_some( ) ,
849+ "Should return Some when all fields already exist"
850+ ) ;
851+
852+ // Verify original values weren't changed
853+ assert_eq ! (
854+ obj. get( "body" ) . unwrap( ) . as_str( ) . unwrap( ) ,
855+ "Atom started successfully."
856+ ) ;
857+ }
858+
517859 #[ test]
518860 fn test_parseable_server_logs ( ) {
519861 let processor = EventProcessor :: new ( FORMATS_JSON ) ;
0 commit comments