33
33
34
34
# This is needed to avoid circular import
35
35
if TYPE_CHECKING :
36
+ from clusterfuzz ._internal .cron .grouper import TestcaseAttributes
36
37
from clusterfuzz ._internal .datastore .data_types import FuzzTarget
37
38
from clusterfuzz ._internal .datastore .data_types import Testcase
38
39
@@ -663,6 +664,7 @@ def log_fatal_and_exit(message, **extras):
663
664
664
665
def get_common_log_context () -> dict [str , str ]:
665
666
"""Return common context to be propagated by logs."""
667
+ # Avoid circular imports on the top level.
666
668
from clusterfuzz ._internal .base import utils
667
669
from clusterfuzz ._internal .system import environment
668
670
@@ -694,6 +696,18 @@ def get_common_log_context() -> dict[str, str]:
694
696
return {}
695
697
696
698
699
+ def get_testcase_id (
700
+ testcase : 'Testcase | TestcaseAttributes' ) -> int | str | None :
701
+ """Return the ID for a testcase or testcase attributes object."""
702
+ # Importing here as 3P libs becomes accessible during runtime, after modules
703
+ # path search is resolved (and logs may be imported before that).
704
+ from google .cloud import ndb
705
+
706
+ if isinstance (testcase , ndb .Model ):
707
+ return testcase .key .id () # type: ignore
708
+ return getattr (testcase , 'id' , None )
709
+
710
+
697
711
class GenericLogStruct (NamedTuple ):
698
712
pass
699
713
@@ -729,6 +743,13 @@ class TestcaseLogStruct(NamedTuple):
729
743
testcase_id : str
730
744
731
745
746
+ class GrouperLogStruct (NamedTuple ):
747
+ testcase_1_id : str
748
+ testcase_1_group : str | int
749
+ testcase_2_id : str
750
+ testcase_2_group : str | int
751
+
752
+
732
753
class LogContextType (enum .Enum ):
733
754
"""Log context types.
734
755
@@ -740,6 +761,7 @@ class LogContextType(enum.Enum):
740
761
FUZZER = 'fuzzer'
741
762
TESTCASE = 'testcase'
742
763
CRON = 'cron'
764
+ GROUPER = 'grouper'
743
765
744
766
def get_extras (self ) -> NamedTuple :
745
767
"""Get the structured log for a given context"""
@@ -793,15 +815,8 @@ def get_extras(self) -> NamedTuple:
793
815
794
816
if self == LogContextType .TESTCASE :
795
817
try :
796
- testcase : 'Testcase | None' = log_contexts .meta .get ('testcase' )
797
- if not testcase :
798
- error (
799
- 'Testcase not found in log context metadata.' ,
800
- ignore_context = True )
801
- return GenericLogStruct ()
802
-
803
- return TestcaseLogStruct (testcase_id = testcase .key .id ()) # type: ignore
804
-
818
+ return TestcaseLogStruct (
819
+ testcase_id = log_contexts .meta .get ('testcase_id' , 'null' ))
805
820
except :
806
821
error (
807
822
'Error retrieving context for testcase-based logs.' ,
@@ -819,6 +834,19 @@ def get_extras(self) -> NamedTuple:
819
834
ignore_context = True )
820
835
return GenericLogStruct ()
821
836
837
+ if self == LogContextType .GROUPER :
838
+ try :
839
+ return GrouperLogStruct (
840
+ testcase_1_id = log_contexts .meta .get ('testcase_1_id' , 'null' ),
841
+ testcase_2_id = log_contexts .meta .get ('testcase_2_id' , 'null' ),
842
+ testcase_1_group = log_contexts .meta .get ('testcase_1_group' , 'null' ),
843
+ testcase_2_group = log_contexts .meta .get ('testcase_2_group' , 'null' ))
844
+ except :
845
+ error (
846
+ 'Error retrieving context for grouper-based logs.' ,
847
+ ignore_context = True )
848
+ return GenericLogStruct ()
849
+
822
850
return GenericLogStruct ()
823
851
824
852
@@ -925,7 +953,7 @@ def fuzzer_log_context(fuzzer_name: str, job_type: str,
925
953
926
954
927
955
@contextlib .contextmanager
928
- def testcase_log_context (testcase : 'Testcase' ,
956
+ def testcase_log_context (testcase : 'Testcase | TestcaseAttributes ' ,
929
957
fuzz_target : 'FuzzTarget | None' ):
930
958
"""Creates a testcase-based context for a given testcase.
931
959
@@ -936,10 +964,12 @@ def testcase_log_context(testcase: 'Testcase',
936
964
with wrap_log_context (
937
965
contexts = [LogContextType .FUZZER , LogContextType .TESTCASE ]):
938
966
try :
939
- log_contexts .add_metadata ('testcase' , testcase ) # type: ignore
967
+ log_contexts .add_metadata ('testcase' , testcase )
940
968
if testcase :
941
- log_contexts .add_metadata ('fuzzer_name' , testcase .fuzzer_name )
942
- log_contexts .add_metadata ('job_type' , testcase .job_type )
969
+ log_contexts .add_metadata ('testcase_id' , get_testcase_id (testcase ))
970
+ log_contexts .add_metadata ('fuzzer_name' ,
971
+ testcase .fuzzer_name ) # type: ignore
972
+ log_contexts .add_metadata ('job_type' , testcase .job_type ) # type: ignore
943
973
if fuzz_target and fuzz_target .binary :
944
974
fuzz_target_bin = fuzz_target .binary
945
975
else :
@@ -955,16 +985,43 @@ def testcase_log_context(testcase: 'Testcase',
955
985
raise e
956
986
finally :
957
987
log_contexts .delete_metadata ('testcase' )
988
+ log_contexts .delete_metadata ('testcase_id' )
958
989
log_contexts .delete_metadata ('fuzzer_name' )
959
990
log_contexts .delete_metadata ('job_type' )
960
991
log_contexts .delete_metadata ('fuzz_target' )
961
992
962
993
963
994
@contextlib .contextmanager
964
995
def cron_log_context ():
996
+ """Creates a cronjob log context, mainly for triage/cleanup tasks."""
965
997
with wrap_log_context (contexts = [LogContextType .CRON ]):
966
998
try :
967
999
yield
968
1000
except Exception as e :
969
1001
warning (message = 'Error during cronjob context.' )
970
1002
raise e
1003
+
1004
+
1005
+ @contextlib .contextmanager
1006
+ def grouper_log_context (testcase_1 : 'Testcase | TestcaseAttributes' ,
1007
+ testcase_2 : 'Testcase | TestcaseAttributes' ):
1008
+ """Creates a grouper context for a given pair of testcases."""
1009
+ with wrap_log_context (contexts = [LogContextType .GROUPER ]):
1010
+ try :
1011
+ if testcase_1 :
1012
+ log_contexts .add_metadata ('testcase_1_id' , get_testcase_id (testcase_1 ))
1013
+ log_contexts .add_metadata ('testcase_1_group' ,
1014
+ getattr (testcase_1 , 'group_id' , 0 ))
1015
+ if testcase_2 :
1016
+ log_contexts .add_metadata ('testcase_2_id' , get_testcase_id (testcase_2 ))
1017
+ log_contexts .add_metadata ('testcase_2_group' ,
1018
+ getattr (testcase_2 , 'group_id' , 0 ))
1019
+ yield
1020
+ except Exception as e :
1021
+ warning (message = 'Error during grouper context.' )
1022
+ raise e
1023
+ finally :
1024
+ log_contexts .delete_metadata ('testcase_1_id' )
1025
+ log_contexts .delete_metadata ('testcase_2_id' )
1026
+ log_contexts .delete_metadata ('testcase_1_group' )
1027
+ log_contexts .delete_metadata ('testcase_2_group' )
0 commit comments