From f8f6b2815257123aa637102df30c6f04e1ef8543 Mon Sep 17 00:00:00 2001 From: tavo Date: Mon, 29 Sep 2025 17:37:59 -0600 Subject: [PATCH] logger --- .clang-format | 804 +++++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 3 + CMakeLists.txt | 28 ++ README.md | 50 ++- slog/slog.h | 501 ++++++++++++++++++++++++++++++ tests/slog.c | 40 +++ 6 files changed, 1425 insertions(+), 1 deletion(-) create mode 100644 .clang-format create mode 100644 CMakeLists.txt create mode 100644 slog/slog.h create mode 100644 tests/slog.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..48405c5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,804 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 11. +# +# For more information, see: +# +# Documentation/dev-tools/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | LC_ALL=C sort -u +ForEachMacros: + - '__ata_qc_for_each' + - '__bio_for_each_bvec' + - '__bio_for_each_segment' + - '__evlist__for_each_entry' + - '__evlist__for_each_entry_continue' + - '__evlist__for_each_entry_from' + - '__evlist__for_each_entry_reverse' + - '__evlist__for_each_entry_safe' + - '__for_each_mem_range' + - '__for_each_mem_range_rev' + - '__for_each_thread' + - '__hlist_for_each_rcu' + - '__map__for_each_symbol_by_name' + - '__pci_bus_for_each_res0' + - '__pci_bus_for_each_res1' + - '__pci_dev_for_each_res0' + - '__pci_dev_for_each_res1' + - '__perf_evlist__for_each_entry' + - '__perf_evlist__for_each_entry_reverse' + - '__perf_evlist__for_each_entry_safe' + - '__rq_for_each_bio' + - '__shost_for_each_device' + - '__sym_for_each' + - '_for_each_counter' + - 'apei_estatus_for_each_section' + - 'ata_for_each_dev' + - 'ata_for_each_link' + - 'ata_qc_for_each' + - 'ata_qc_for_each_raw' + - 'ata_qc_for_each_with_internal' + - 'ax25_for_each' + - 'ax25_uid_for_each' + - 'bio_for_each_bvec' + - 'bio_for_each_bvec_all' + - 'bio_for_each_folio_all' + - 'bio_for_each_integrity_vec' + - 'bio_for_each_segment' + - 'bio_for_each_segment_all' + - 'bio_list_for_each' + - 'bip_for_each_vec' + - 'bond_for_each_slave' + - 'bond_for_each_slave_rcu' + - 'bpf_for_each' + - 'bpf_for_each_reg_in_vstate' + - 'bpf_for_each_reg_in_vstate_mask' + - 'bpf_for_each_spilled_reg' + - 'bpf_object__for_each_map' + - 'bpf_object__for_each_program' + - 'btree_for_each_safe128' + - 'btree_for_each_safe32' + - 'btree_for_each_safe64' + - 'btree_for_each_safel' + - 'card_for_each_dev' + - 'cgroup_taskset_for_each' + - 'cgroup_taskset_for_each_leader' + - 'cpu_aggr_map__for_each_idx' + - 'cpufreq_for_each_efficient_entry_idx' + - 'cpufreq_for_each_entry' + - 'cpufreq_for_each_entry_idx' + - 'cpufreq_for_each_valid_entry' + - 'cpufreq_for_each_valid_entry_idx' + - 'css_for_each_child' + - 'css_for_each_descendant_post' + - 'css_for_each_descendant_pre' + - 'damon_for_each_region' + - 'damon_for_each_region_from' + - 'damon_for_each_region_safe' + - 'damon_for_each_scheme' + - 'damon_for_each_scheme_safe' + - 'damon_for_each_target' + - 'damon_for_each_target_safe' + - 'damos_for_each_filter' + - 'damos_for_each_filter_safe' + - 'damos_for_each_ops_filter' + - 'damos_for_each_ops_filter_safe' + - 'damos_for_each_quota_goal' + - 'damos_for_each_quota_goal_safe' + - 'data__for_each_file' + - 'data__for_each_file_new' + - 'data__for_each_file_start' + - 'def_for_each_cpu' + - 'device_for_each_child_node' + - 'device_for_each_child_node_scoped' + - 'dma_fence_array_for_each' + - 'dma_fence_chain_for_each' + - 'dma_fence_unwrap_for_each' + - 'dma_resv_for_each_fence' + - 'dma_resv_for_each_fence_unlocked' + - 'do_for_each_ftrace_op' + - 'drm_atomic_crtc_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane_state' + - 'drm_atomic_for_each_plane_damage' + - 'drm_client_for_each_connector_iter' + - 'drm_client_for_each_modeset' + - 'drm_connector_for_each_possible_encoder' + - 'drm_exec_for_each_locked_object' + - 'drm_exec_for_each_locked_object_reverse' + - 'drm_for_each_bridge_in_chain' + - 'drm_for_each_connector_iter' + - 'drm_for_each_crtc' + - 'drm_for_each_crtc_reverse' + - 'drm_for_each_encoder' + - 'drm_for_each_encoder_mask' + - 'drm_for_each_fb' + - 'drm_for_each_legacy_plane' + - 'drm_for_each_plane' + - 'drm_for_each_plane_mask' + - 'drm_for_each_privobj' + - 'drm_gem_for_each_gpuvm_bo' + - 'drm_gem_for_each_gpuvm_bo_safe' + - 'drm_gpusvm_for_each_range' + - 'drm_gpuva_for_each_op' + - 'drm_gpuva_for_each_op_from_reverse' + - 'drm_gpuva_for_each_op_reverse' + - 'drm_gpuva_for_each_op_safe' + - 'drm_gpuvm_bo_for_each_va' + - 'drm_gpuvm_bo_for_each_va_safe' + - 'drm_gpuvm_for_each_va' + - 'drm_gpuvm_for_each_va_range' + - 'drm_gpuvm_for_each_va_range_safe' + - 'drm_gpuvm_for_each_va_safe' + - 'drm_mm_for_each_hole' + - 'drm_mm_for_each_node' + - 'drm_mm_for_each_node_in_range' + - 'drm_mm_for_each_node_safe' + - 'dsa_switch_for_each_available_port' + - 'dsa_switch_for_each_cpu_port' + - 'dsa_switch_for_each_cpu_port_continue_reverse' + - 'dsa_switch_for_each_port' + - 'dsa_switch_for_each_port_continue_reverse' + - 'dsa_switch_for_each_port_safe' + - 'dsa_switch_for_each_user_port' + - 'dsa_switch_for_each_user_port_continue_reverse' + - 'dsa_tree_for_each_cpu_port' + - 'dsa_tree_for_each_user_port' + - 'dsa_tree_for_each_user_port_continue_reverse' + - 'dso__for_each_symbol' + - 'elf_hash_for_each_possible' + - 'elf_symtab__for_each_symbol' + - 'evlist__for_each_cpu' + - 'evlist__for_each_entry' + - 'evlist__for_each_entry_continue' + - 'evlist__for_each_entry_from' + - 'evlist__for_each_entry_reverse' + - 'evlist__for_each_entry_safe' + - 'flow_action_for_each' + - 'for_each_acpi_consumer_dev' + - 'for_each_acpi_dev_match' + - 'for_each_active_dev_scope' + - 'for_each_active_drhd_unit' + - 'for_each_active_iommu' + - 'for_each_active_irq' + - 'for_each_active_route' + - 'for_each_aggr_pgid' + - 'for_each_alloc_capable_rdt_resource' + - 'for_each_and_bit' + - 'for_each_andnot_bit' + - 'for_each_available_child_of_node' + - 'for_each_available_child_of_node_scoped' + - 'for_each_bench' + - 'for_each_bio' + - 'for_each_board_func_rsrc' + - 'for_each_btf_ext_rec' + - 'for_each_btf_ext_sec' + - 'for_each_bvec' + - 'for_each_capable_rdt_resource' + - 'for_each_card_auxs' + - 'for_each_card_auxs_safe' + - 'for_each_card_components' + - 'for_each_card_dapms' + - 'for_each_card_pre_auxs' + - 'for_each_card_prelinks' + - 'for_each_card_rtds' + - 'for_each_card_rtds_safe' + - 'for_each_card_widgets' + - 'for_each_card_widgets_safe' + - 'for_each_cgroup_storage_type' + - 'for_each_child_of_node' + - 'for_each_child_of_node_scoped' + - 'for_each_child_of_node_with_prefix' + - 'for_each_clear_bit' + - 'for_each_clear_bit_from' + - 'for_each_clear_bitrange' + - 'for_each_clear_bitrange_from' + - 'for_each_cmd' + - 'for_each_cmsghdr' + - 'for_each_collection' + - 'for_each_comp_order' + - 'for_each_compatible_node' + - 'for_each_component_dais' + - 'for_each_component_dais_safe' + - 'for_each_conduit' + - 'for_each_console' + - 'for_each_console_srcu' + - 'for_each_cpu' + - 'for_each_cpu_and' + - 'for_each_cpu_andnot' + - 'for_each_cpu_from' + - 'for_each_cpu_or' + - 'for_each_cpu_wrap' + - 'for_each_dapm_widgets' + - 'for_each_dedup_cand' + - 'for_each_dev_addr' + - 'for_each_dev_scope' + - 'for_each_dma_cap_mask' + - 'for_each_dpcm_be' + - 'for_each_dpcm_be_rollback' + - 'for_each_dpcm_be_safe' + - 'for_each_dpcm_fe' + - 'for_each_drhd_unit' + - 'for_each_dss_dev' + - 'for_each_efi_memory_desc' + - 'for_each_efi_memory_desc_in_map' + - 'for_each_element' + - 'for_each_element_extid' + - 'for_each_element_id' + - 'for_each_enabled_cpu' + - 'for_each_endpoint_of_node' + - 'for_each_event' + - 'for_each_event_tps' + - 'for_each_evictable_lru' + - 'for_each_fib6_node_rt_rcu' + - 'for_each_fib6_walker_rt' + - 'for_each_file_lock' + - 'for_each_free_mem_pfn_range_in_zone_from' + - 'for_each_free_mem_range' + - 'for_each_free_mem_range_reverse' + - 'for_each_func_rsrc' + - 'for_each_gpiochip_node' + - 'for_each_group_evsel' + - 'for_each_group_evsel_head' + - 'for_each_group_member' + - 'for_each_group_member_head' + - 'for_each_hstate' + - 'for_each_hwgpio' + - 'for_each_hwgpio_in_range' + - 'for_each_if' + - 'for_each_inject_fn' + - 'for_each_insn' + - 'for_each_insn_op_loc' + - 'for_each_insn_prefix' + - 'for_each_intid' + - 'for_each_iommu' + - 'for_each_ip_tunnel_rcu' + - 'for_each_irq_desc' + - 'for_each_irq_nr' + - 'for_each_lang' + - 'for_each_link_ch_maps' + - 'for_each_link_codecs' + - 'for_each_link_cpus' + - 'for_each_link_platforms' + - 'for_each_lru' + - 'for_each_matching_node' + - 'for_each_matching_node_and_match' + - 'for_each_media_entity_data_link' + - 'for_each_mem_pfn_range' + - 'for_each_mem_range' + - 'for_each_mem_range_rev' + - 'for_each_mem_region' + - 'for_each_member' + - 'for_each_memory' + - 'for_each_migratetype_order' + - 'for_each_missing_reg' + - 'for_each_mle_subelement' + - 'for_each_mod_mem_type' + - 'for_each_mon_capable_rdt_resource' + - 'for_each_mp_bvec' + - 'for_each_net' + - 'for_each_net_continue_reverse' + - 'for_each_net_rcu' + - 'for_each_netdev' + - 'for_each_netdev_continue' + - 'for_each_netdev_continue_rcu' + - 'for_each_netdev_continue_reverse' + - 'for_each_netdev_dump' + - 'for_each_netdev_feature' + - 'for_each_netdev_in_bond_rcu' + - 'for_each_netdev_rcu' + - 'for_each_netdev_reverse' + - 'for_each_netdev_safe' + - 'for_each_new_connector_in_state' + - 'for_each_new_crtc_in_state' + - 'for_each_new_mst_mgr_in_state' + - 'for_each_new_plane_in_state' + - 'for_each_new_plane_in_state_reverse' + - 'for_each_new_private_obj_in_state' + - 'for_each_new_reg' + - 'for_each_nhlt_endpoint' + - 'for_each_nhlt_endpoint_fmtcfg' + - 'for_each_nhlt_fmtcfg' + - 'for_each_node' + - 'for_each_node_by_name' + - 'for_each_node_by_type' + - 'for_each_node_mask' + - 'for_each_node_numadist' + - 'for_each_node_state' + - 'for_each_node_with_cpus' + - 'for_each_node_with_property' + - 'for_each_nonreserved_multicast_dest_pgid' + - 'for_each_numa_hop_mask' + - 'for_each_of_allnodes' + - 'for_each_of_allnodes_from' + - 'for_each_of_cpu_node' + - 'for_each_of_graph_port' + - 'for_each_of_graph_port_endpoint' + - 'for_each_of_pci_range' + - 'for_each_old_connector_in_state' + - 'for_each_old_crtc_in_state' + - 'for_each_old_mst_mgr_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' + - 'for_each_oldnew_connector_in_state' + - 'for_each_oldnew_crtc_in_state' + - 'for_each_oldnew_mst_mgr_in_state' + - 'for_each_oldnew_plane_in_state' + - 'for_each_oldnew_plane_in_state_reverse' + - 'for_each_oldnew_private_obj_in_state' + - 'for_each_online_cpu' + - 'for_each_online_cpu_wrap' + - 'for_each_online_node' + - 'for_each_online_pgdat' + - 'for_each_or_bit' + - 'for_each_page_ext' + - 'for_each_path' + - 'for_each_pci_bridge' + - 'for_each_pci_dev' + - 'for_each_pcm_streams' + - 'for_each_physmem_range' + - 'for_each_populated_zone' + - 'for_each_possible_cpu' + - 'for_each_possible_cpu_wrap' + - 'for_each_present_blessed_reg' + - 'for_each_present_cpu' + - 'for_each_present_section_nr' + - 'for_each_prime_number' + - 'for_each_prime_number_from' + - 'for_each_probe_cache_entry' + - 'for_each_process' + - 'for_each_process_thread' + - 'for_each_prop_codec_conf' + - 'for_each_prop_dai_codec' + - 'for_each_prop_dai_cpu' + - 'for_each_prop_dlc_codecs' + - 'for_each_prop_dlc_cpus' + - 'for_each_prop_dlc_platforms' + - 'for_each_property_of_node' + - 'for_each_rdt_resource' + - 'for_each_reg' + - 'for_each_reg_filtered' + - 'for_each_reloc' + - 'for_each_reloc_from' + - 'for_each_requested_gpio' + - 'for_each_requested_gpio_in_range' + - 'for_each_reserved_child_of_node' + - 'for_each_reserved_mem_range' + - 'for_each_reserved_mem_region' + - 'for_each_rtd_ch_maps' + - 'for_each_rtd_codec_dais' + - 'for_each_rtd_components' + - 'for_each_rtd_cpu_dais' + - 'for_each_rtd_dais' + - 'for_each_rtd_dais_reverse' + - 'for_each_sband_iftype_data' + - 'for_each_script' + - 'for_each_sec' + - 'for_each_set_bit' + - 'for_each_set_bit_from' + - 'for_each_set_bit_wrap' + - 'for_each_set_bitrange' + - 'for_each_set_bitrange_from' + - 'for_each_set_clump8' + - 'for_each_sg' + - 'for_each_sg_dma_page' + - 'for_each_sg_page' + - 'for_each_sgtable_dma_page' + - 'for_each_sgtable_dma_sg' + - 'for_each_sgtable_page' + - 'for_each_sgtable_sg' + - 'for_each_sibling_event' + - 'for_each_sta_active_link' + - 'for_each_subelement' + - 'for_each_subelement_extid' + - 'for_each_subelement_id' + - 'for_each_sublist' + - 'for_each_subsystem' + - 'for_each_suite' + - 'for_each_supported_activate_fn' + - 'for_each_supported_inject_fn' + - 'for_each_sym' + - 'for_each_thread' + - 'for_each_token' + - 'for_each_unicast_dest_pgid' + - 'for_each_valid_link' + - 'for_each_vif_active_link' + - 'for_each_vma' + - 'for_each_vma_range' + - 'for_each_vsi' + - 'for_each_wakeup_source' + - 'for_each_zone' + - 'for_each_zone_zonelist' + - 'for_each_zone_zonelist_nodemask' + - 'func_for_each_insn' + - 'fwnode_for_each_available_child_node' + - 'fwnode_for_each_child_node' + - 'fwnode_for_each_parent_node' + - 'fwnode_graph_for_each_endpoint' + - 'gadget_for_each_ep' + - 'genradix_for_each' + - 'genradix_for_each_from' + - 'genradix_for_each_reverse' + - 'hash_for_each' + - 'hash_for_each_possible' + - 'hash_for_each_possible_rcu' + - 'hash_for_each_possible_rcu_notrace' + - 'hash_for_each_possible_safe' + - 'hash_for_each_rcu' + - 'hash_for_each_safe' + - 'hashmap__for_each_entry' + - 'hashmap__for_each_entry_safe' + - 'hashmap__for_each_key_entry' + - 'hashmap__for_each_key_entry_safe' + - 'hctx_for_each_ctx' + - 'hists__for_each_format' + - 'hists__for_each_sort_list' + - 'hlist_bl_for_each_entry' + - 'hlist_bl_for_each_entry_rcu' + - 'hlist_bl_for_each_entry_safe' + - 'hlist_for_each' + - 'hlist_for_each_entry' + - 'hlist_for_each_entry_continue' + - 'hlist_for_each_entry_continue_rcu' + - 'hlist_for_each_entry_continue_rcu_bh' + - 'hlist_for_each_entry_from' + - 'hlist_for_each_entry_from_rcu' + - 'hlist_for_each_entry_rcu' + - 'hlist_for_each_entry_rcu_bh' + - 'hlist_for_each_entry_rcu_notrace' + - 'hlist_for_each_entry_safe' + - 'hlist_for_each_entry_srcu' + - 'hlist_for_each_safe' + - 'hlist_nulls_for_each_entry' + - 'hlist_nulls_for_each_entry_from' + - 'hlist_nulls_for_each_entry_rcu' + - 'hlist_nulls_for_each_entry_safe' + - 'i3c_bus_for_each_i2cdev' + - 'i3c_bus_for_each_i3cdev' + - 'idr_for_each_entry' + - 'idr_for_each_entry_continue' + - 'idr_for_each_entry_continue_ul' + - 'idr_for_each_entry_ul' + - 'iio_for_each_active_channel' + - 'in_dev_for_each_ifa_rcu' + - 'in_dev_for_each_ifa_rtnl' + - 'in_dev_for_each_ifa_rtnl_net' + - 'inet_bind_bucket_for_each' + - 'interval_tree_for_each_span' + - 'intlist__for_each_entry' + - 'intlist__for_each_entry_safe' + - 'kcore_copy__for_each_phdr' + - 'key_for_each' + - 'key_for_each_safe' + - 'klp_for_each_func' + - 'klp_for_each_func_safe' + - 'klp_for_each_func_static' + - 'klp_for_each_object' + - 'klp_for_each_object_safe' + - 'klp_for_each_object_static' + - 'kunit_suite_for_each_test_case' + - 'kvm_for_each_memslot' + - 'kvm_for_each_memslot_in_gfn_range' + - 'kvm_for_each_vcpu' + - 'libbpf_nla_for_each_attr' + - 'list_for_each' + - 'list_for_each_codec' + - 'list_for_each_codec_safe' + - 'list_for_each_continue' + - 'list_for_each_entry' + - 'list_for_each_entry_continue' + - 'list_for_each_entry_continue_rcu' + - 'list_for_each_entry_continue_reverse' + - 'list_for_each_entry_from' + - 'list_for_each_entry_from_rcu' + - 'list_for_each_entry_from_reverse' + - 'list_for_each_entry_lockless' + - 'list_for_each_entry_rcu' + - 'list_for_each_entry_reverse' + - 'list_for_each_entry_safe' + - 'list_for_each_entry_safe_continue' + - 'list_for_each_entry_safe_from' + - 'list_for_each_entry_safe_reverse' + - 'list_for_each_entry_srcu' + - 'list_for_each_from' + - 'list_for_each_prev' + - 'list_for_each_prev_safe' + - 'list_for_each_rcu' + - 'list_for_each_safe' + - 'llist_for_each' + - 'llist_for_each_entry' + - 'llist_for_each_entry_safe' + - 'llist_for_each_safe' + - 'lwq_for_each_safe' + - 'map__for_each_symbol' + - 'map__for_each_symbol_by_name' + - 'mas_for_each' + - 'mas_for_each_rev' + - 'mci_for_each_dimm' + - 'media_device_for_each_entity' + - 'media_device_for_each_intf' + - 'media_device_for_each_link' + - 'media_device_for_each_pad' + - 'media_entity_for_each_pad' + - 'media_pipeline_for_each_entity' + - 'media_pipeline_for_each_pad' + - 'mlx5_lag_for_each_peer_mdev' + - 'mptcp_for_each_subflow' + - 'msi_domain_for_each_desc' + - 'msi_for_each_desc' + - 'mt_for_each' + - 'nanddev_io_for_each_block' + - 'nanddev_io_for_each_page' + - 'neigh_for_each_in_bucket' + - 'neigh_for_each_in_bucket_rcu' + - 'neigh_for_each_in_bucket_safe' + - 'netdev_for_each_lower_dev' + - 'netdev_for_each_lower_private' + - 'netdev_for_each_lower_private_rcu' + - 'netdev_for_each_mc_addr' + - 'netdev_for_each_synced_mc_addr' + - 'netdev_for_each_synced_uc_addr' + - 'netdev_for_each_uc_addr' + - 'netdev_for_each_upper_dev_rcu' + - 'netdev_hw_addr_list_for_each' + - 'nft_rule_for_each_expr' + - 'nla_for_each_attr' + - 'nla_for_each_attr_type' + - 'nla_for_each_nested' + - 'nla_for_each_nested_type' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + - 'nr_neigh_for_each' + - 'nr_neigh_for_each_safe' + - 'nr_node_for_each' + - 'nr_node_for_each_safe' + - 'of_for_each_phandle' + - 'of_property_for_each_string' + - 'of_property_for_each_u32' + - 'pci_bus_for_each_resource' + - 'pci_dev_for_each_resource' + - 'pcl_for_each_chunk' + - 'pcl_for_each_segment' + - 'pcm_for_each_format' + - 'perf_config_items__for_each_entry' + - 'perf_config_sections__for_each_entry' + - 'perf_config_set__for_each_entry' + - 'perf_cpu_map__for_each_cpu' + - 'perf_cpu_map__for_each_cpu_skip_any' + - 'perf_cpu_map__for_each_idx' + - 'perf_evlist__for_each_entry' + - 'perf_evlist__for_each_entry_reverse' + - 'perf_evlist__for_each_entry_safe' + - 'perf_evlist__for_each_evsel' + - 'perf_evlist__for_each_mmap' + - 'perf_evsel_for_each_per_thread_period_safe' + - 'perf_hpp_list__for_each_format' + - 'perf_hpp_list__for_each_format_safe' + - 'perf_hpp_list__for_each_sort_list' + - 'perf_hpp_list__for_each_sort_list_safe' + - 'plist_for_each' + - 'plist_for_each_continue' + - 'plist_for_each_entry' + - 'plist_for_each_entry_continue' + - 'plist_for_each_entry_safe' + - 'plist_for_each_safe' + - 'pnp_for_each_card' + - 'pnp_for_each_dev' + - 'protocol_for_each_card' + - 'protocol_for_each_dev' + - 'queue_for_each_hw_ctx' + - 'radix_tree_for_each_slot' + - 'radix_tree_for_each_tagged' + - 'rb_for_each' + - 'rbtree_postorder_for_each_entry_safe' + - 'rdma_for_each_block' + - 'rdma_for_each_port' + - 'rdma_umem_for_each_dma_block' + - 'resource_list_for_each_entry' + - 'resource_list_for_each_entry_safe' + - 'rhl_for_each_entry_rcu' + - 'rhl_for_each_rcu' + - 'rht_for_each' + - 'rht_for_each_entry' + - 'rht_for_each_entry_from' + - 'rht_for_each_entry_rcu' + - 'rht_for_each_entry_rcu_from' + - 'rht_for_each_entry_safe' + - 'rht_for_each_from' + - 'rht_for_each_rcu' + - 'rht_for_each_rcu_from' + - 'rq_for_each_bvec' + - 'rq_for_each_segment' + - 'rq_list_for_each' + - 'rq_list_for_each_safe' + - 'sample_read_group__for_each' + - 'scsi_for_each_prot_sg' + - 'scsi_for_each_sg' + - 'sctp_for_each_hentry' + - 'sctp_skb_for_each' + - 'sec_for_each_insn' + - 'sec_for_each_insn_continue' + - 'sec_for_each_insn_from' + - 'sec_for_each_sym' + - 'shdma_for_each_chan' + - 'shost_for_each_device' + - 'sk_for_each' + - 'sk_for_each_bound' + - 'sk_for_each_bound_safe' + - 'sk_for_each_entry_offset_rcu' + - 'sk_for_each_from' + - 'sk_for_each_rcu' + - 'sk_for_each_safe' + - 'sk_nulls_for_each' + - 'sk_nulls_for_each_from' + - 'sk_nulls_for_each_rcu' + - 'snd_array_for_each' + - 'snd_pcm_group_for_each_entry' + - 'snd_soc_dapm_widget_for_each_path' + - 'snd_soc_dapm_widget_for_each_path_safe' + - 'snd_soc_dapm_widget_for_each_sink_path' + - 'snd_soc_dapm_widget_for_each_source_path' + - 'sparsebit_for_each_set_range' + - 'strlist__for_each_entry' + - 'strlist__for_each_entry_safe' + - 'sym_for_each_insn' + - 'sym_for_each_insn_continue_reverse' + - 'symbols__for_each_entry' + - 'tb_property_for_each' + - 'tcf_act_for_each_action' + - 'tcf_exts_for_each_action' + - 'test_suite__for_each_test_case' + - 'tool_pmu__for_each_event' + - 'ttm_bo_lru_for_each_reserved_guarded' + - 'ttm_resource_manager_for_each_res' + - 'udp_lrpa_for_each_entry_rcu' + - 'udp_portaddr_for_each_entry' + - 'udp_portaddr_for_each_entry_rcu' + - 'usb_hub_for_each_child' + - 'v4l2_device_for_each_subdev' + - 'v4l2_m2m_for_each_dst_buf' + - 'v4l2_m2m_for_each_dst_buf_safe' + - 'v4l2_m2m_for_each_src_buf' + - 'v4l2_m2m_for_each_src_buf_safe' + - 'virtio_device_for_each_vq' + - 'vkms_config_for_each_connector' + - 'vkms_config_for_each_crtc' + - 'vkms_config_for_each_encoder' + - 'vkms_config_for_each_plane' + - 'vkms_config_connector_for_each_possible_encoder' + - 'vkms_config_encoder_for_each_possible_crtc' + - 'vkms_config_plane_for_each_possible_crtc' + - 'while_for_each_ftrace_op' + - 'workloads__for_each' + - 'xa_for_each' + - 'xa_for_each_marked' + - 'xa_for_each_range' + - 'xa_for_each_start' + - 'xas_for_each' + - 'xas_for_each_conflict' + - 'xas_for_each_marked' + - 'xbc_array_for_each_value' + - 'xbc_for_each_key_value' + - 'xbc_node_for_each_array_value' + - 'xbc_node_for_each_child' + - 'xbc_node_for_each_key_value' + - 'xbc_node_for_each_subkey' + - 'ynl_attr_for_each' + - 'ynl_attr_for_each_nested' + - 'ynl_attr_for_each_payload' + - 'zorro_for_each_dev' + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.gitignore b/.gitignore index 4fa1c75..dc727f2 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ CTestTestfile.cmake _deps CMakeUserPresets.json +.cache/ +build/ +slog_tests diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fbd73ae --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.15) +project(slog C) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if (MSVC) + add_compile_options(/W4) +else() + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +enable_testing() + +add_executable(slog_tests + tests/slog.c +) + +target_compile_options(slog_tests + PRIVATE + -fmacro-prefix-map=${CMAKE_SOURCE_DIR}/= +) + +target_include_directories(slog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +add_test(NAME slog_tests COMMAND slog_tests) diff --git a/README.md b/README.md index dc65270..a493373 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,51 @@ # slog -Single-header structured logger in C \ No newline at end of file +Single-header structured logger in C99 + +```c +#include +#include + +int main(void) +{ + struct SlogHandlerOpts opts1 = { + .File = stdout, + .MinLevel = SlogLevelDebug, + .Prefix = NULL, + .AddSource = true, + .Json = false, + }; + + struct SlogLogger l1 = { + SlogTextHandler, + &opts1, + }; + + struct SlogHandlerOpts opts2 = { + .File = stderr, + .MinLevel = SlogLevelError, + .Prefix = "scope", + .AddSource = true, + .Json = true, + }; + + struct SlogLogger l2 = { + SlogTextHandler, + &opts2, + }; + + struct SlogAttr attrs[] = { + SlogAttrB("ok", true), + }; + + SLOG_DEBUG(&l1, "hello", attrs); + // Written to stdout: + // time=2025-09-29T17:36:08.597673657-06:00 level=INFO msg=hello source=tests/slog.c:36 func=main ok=true + + SLOG_ERROR(&l2, "hello", attrs); + // Written to stderr: + //time=2025-09-29T17:36:08.597771088-06:00 level=ERROR msg=hello scope.source=tests/slog.c:37 scope.func=main scope.ok=true + + return 0; +} +``` diff --git a/slog/slog.h b/slog/slog.h new file mode 100644 index 0000000..683f83d --- /dev/null +++ b/slog/slog.h @@ -0,0 +1,501 @@ +#ifndef SLOG_H +#define SLOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 199309L +#endif +#include + +#include +#include +#include +#include +#include +#include +#include + +static const char *SlogTimeKey = "time"; +static const char *SlogLevelKey = "level"; +static const char *SlogMessageKey = "msg"; +static const char *SlogSourceKey = "source"; +static const char *SlogFuncKey = "func"; + +typedef int SlogLevel; + +static const SlogLevel SlogLevelDebug = -4; +static const SlogLevel SlogLevelInfo = 0; +static const SlogLevel SlogLevelWarn = 4; +static const SlogLevel SlogLevelError = 8; + +typedef enum slogAnyTag { + slogAnyNull, + slogAnyInt, + slogAnyFloat, + slogAnyString, + slogAnyBool, + slogAnyTime, + slogAnyPtr, + slogAnyBytes, +} slogAnyTag; + +typedef struct SlogAttr { + const char *key; + slogAnyTag tag; + union { + void *as_null; + int64_t as_int; + double as_float; + const char *as_str; + bool as_bool; + struct timespec as_time; + void *as_ptr; + struct { + const void *data; + size_t size; + } as_bytes; + } value; +} SlogAttr; + +typedef struct SlogSource { + const char *File; + const char *Func; + int Line; +} SlogSource; + +typedef struct SlogHandlerOpts { + FILE *File; + SlogLevel MinLevel; + const char *Prefix; + bool AddSource; + bool Json; +} SlogHandlerOpts; + +typedef struct SlogLogger { + void (*Handler)(struct SlogLogger *self, SlogLevel level, + const char *msg, const SlogAttr *attrs, int attrCount, + const SlogSource *src); + SlogHandlerOpts *Opts; +} SlogLogger; + +static int slogUtcOffsetMinutes(time_t t) +{ + struct tm lt, gt; + localtime_r(&t, <); + gmtime_r(&t, >); + + time_t utc_as_local = mktime(>); + long off_seconds = (long)difftime(t, utc_as_local); + return (int)(off_seconds / 60); +} + +static inline void slogRealtimeTimespec(struct timespec *ts) +{ +#if defined(_WIN32) + FILETIME ft; + // Win8+: GetSystemTimePreciseAsFileTime; older: GetSystemTimeAsFileTime + static BOOL(WINAPI * pGetPrecise)(LPFILETIME) = NULL; + static int resolved = 0; + if (!resolved) { + HMODULE h = GetModuleHandleA("kernel32.dll"); + pGetPrecise = (BOOL(WINAPI *)(LPFILETIME))GetProcAddress( + h, "GetSystemTimePreciseAsFileTime"); + resolved = 1; + } + if (pGetPrecise) + pGetPrecise(&ft); + else + GetSystemTimeAsFileTime(&ft); + + // FILETIME: 100-ns ticks since 1601-01-01 + const uint64_t EPOCH_DIFF_100NS = + 116444736000000000ULL; // to 1970-01-01 + uint64_t t100 = ((uint64_t)ft.dwHighDateTime << 32) | + (uint64_t)ft.dwLowDateTime; + uint64_t t100_unix = t100 - EPOCH_DIFF_100NS; + + uint64_t ns = t100_unix * 100ULL; // to nanoseconds + ts->tv_sec = (time_t)(ns / 1000000000ULL); + ts->tv_nsec = (long)(ns % 1000000000ULL); +#else + clock_gettime(CLOCK_REALTIME, ts); +#endif +} + +static inline void slogFormatTimespec(char *buf, size_t buflen, + const struct timespec *ts) +{ + if (!buf || buflen == 0) + return; + + if (!ts) { // (optional) write "null" for null pointer + (void)snprintf(buf, buflen, "null"); + return; + } + + struct tm tm_info; + time_t secs = (time_t)ts->tv_sec; + localtime_r(&secs, &tm_info); + + char datetime[32]; + strftime(datetime, sizeof datetime, "%Y-%m-%dT%H:%M:%S", &tm_info); + + char tz[7]; // +HH:MM +#if defined(_WIN32) + TIME_ZONE_INFORMATION tzi; + DWORD id = GetTimeZoneInformation(&tzi); + LONG bias = tzi.Bias; + if (id == TIME_ZONE_ID_DAYLIGHT) + bias += tzi.DaylightBias; + else if (id == TIME_ZONE_ID_STANDARD) + bias += tzi.StandardBias; + + /* local - UTC in minutes (east positive) */ + long off_min = -(long)bias; + + int sign = (off_min >= 0) ? '+' : '-'; + int ah = (int)((off_min >= 0 ? off_min : -off_min) / 60); + int am = (int)((off_min >= 0 ? off_min : -off_min) % 60); + if (ah > 99) + ah = 99; + snprintf(tz, sizeof tz, "%c%02d:%02d", sign, ah, am); +#else + long off_min = (long)slogUtcOffsetMinutes(secs); + int sign = (off_min >= 0) ? '+' : '-'; + int ah = (int)((off_min >= 0 ? off_min : -off_min) / 60); + int am = (int)((off_min >= 0 ? off_min : -off_min) % 60); + if (ah > 99) + ah = 99; + snprintf(tz, sizeof tz, "%c%02d:%02d", sign, ah, am); +#endif + + snprintf(buf, buflen, "%s.%09ld%s", datetime, (long)ts->tv_nsec, tz); +} + +static inline void slogTimestamp(char *buf, size_t buflen) +{ + if (!buf || buflen == 0) + return; + + struct timespec ts; + slogRealtimeTimespec(&ts); + slogFormatTimespec(buf, buflen, &ts); +} + +static inline SlogAttr SlogAttrNull(const char *key) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyNull; + a.value.as_null = NULL; + return a; +} + +static inline SlogAttr SlogAttrI(const char *key, int64_t n) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyInt; + a.value.as_int = n; + return a; +} + +static inline SlogAttr SlogAttrF(const char *key, double n) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyFloat; + a.value.as_float = n; + return a; +} + +static inline SlogAttr SlogAttrS(const char *key, const char *s) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyString; + a.value.as_str = s; + return a; +} + +static inline SlogAttr SlogAttrB(const char *key, bool b) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyBool; + a.value.as_bool = b; + return a; +} + +static inline SlogAttr SlogAttrT(const char *key, struct timespec t) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyTime; + a.value.as_time = t; + return a; +} + +static inline SlogAttr SlogAttrPtr(const char *key, void *ptr) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyPtr; + a.value.as_ptr = ptr; + return a; +} + +static inline SlogAttr SlogAttrBytes(const char *key, const void *data, + size_t size) +{ + SlogAttr a; + a.key = key; + a.tag = slogAnyBytes; + a.value.as_bytes.data = data; + a.value.as_bytes.size = size; + return a; +} + +static inline void SlogLevelStringWithDiff(const char *base, int diff, + char *buf, size_t buflen) +{ + if (!buf || buflen == 0) + return; + buf[0] = '\0'; + + if (!base) + base = "LV"; + + if (diff == 0) { + snprintf(buf, buflen, "%s", base); + } else { + snprintf(buf, buflen, "%s%+d", base, diff); + } +} + +static inline void SlogLevelString(int l, char *buf, size_t buflen) +{ + if (!buf || buflen == 0) + return; + buf[0] = '\0'; + + const char *base; + int diff; + + if (l < SlogLevelInfo) { + base = "DEBUG"; + diff = l - SlogLevelDebug; + } else if (l < SlogLevelWarn) { + base = "INFO"; + diff = l - SlogLevelInfo; + } else if (l < SlogLevelError) { + base = "WARN"; + diff = l - SlogLevelWarn; + } else { + base = "ERROR"; + diff = l - SlogLevelError; + } + + SlogLevelStringWithDiff(base, diff, buf, buflen); +} + +static inline void buf_appendf(char *dst, size_t dstsz, size_t *idx, + const char *fmt, ...) +{ + if (!dst || !dstsz || !idx || !fmt) + return; + size_t i = *idx; + if (i >= dstsz) + return; + size_t rem = dstsz - i; + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(dst + i, rem, fmt, ap); + va_end(ap); + if (n < 0) + return; + size_t wrote = (size_t)n; + *idx += wrote >= rem ? rem : wrote; +} + +static inline void SlogTextHandler(struct SlogLogger *self, SlogLevel level, + const char *msg, const SlogAttr *attrs, + int attrCount, const SlogSource *src) +{ + if (self && self->Opts) { + if (level < self->Opts->MinLevel) { + return; + } + } + + FILE *out = stdout; + const char *prefix = NULL; + bool add_source = false; + + if (self && self->Opts) { + if (self->Opts->File) + out = self->Opts->File; + prefix = self->Opts->Prefix; + add_source = self->Opts->AddSource; + } + + char tbuf[64]; + slogTimestamp(tbuf, sizeof tbuf); + + char nbuf[16]; + SlogLevelString(level, nbuf, sizeof nbuf); + + char line[2048]; + size_t i = 0; + + buf_appendf(line, sizeof line, &i, "%s=%s %s=%s %s=%s", SlogTimeKey, + tbuf, SlogLevelKey, nbuf, SlogMessageKey, msg ? msg : ""); + + if (add_source && src && src->File) { + buf_appendf(line, sizeof line, &i, " "); + if (prefix && *prefix) + buf_appendf(line, sizeof line, &i, "%s.", prefix); + buf_appendf(line, sizeof line, &i, "%s=%s:%d", SlogSourceKey, + src->File, src->Line); + + if (src->Func && *src->Func) { + buf_appendf(line, sizeof line, &i, " "); + if (prefix && *prefix) + buf_appendf(line, sizeof line, &i, "%s.", + prefix); + buf_appendf(line, sizeof line, &i, "%s=%s", SlogFuncKey, + src->Func); + } + } + + for (int a = 0; a < attrCount; ++a) { + const SlogAttr *at = &attrs[a]; + const char *k = at->key ? at->key : "attr"; + + buf_appendf(line, sizeof line, &i, " "); + if (prefix && *prefix) + buf_appendf(line, sizeof line, &i, "%s.", prefix); + buf_appendf(line, sizeof line, &i, "%s=", k); + + switch (at->tag) { + case slogAnyNull: + buf_appendf(line, sizeof line, &i, "null"); + break; + case slogAnyInt: + buf_appendf(line, sizeof line, &i, "%" PRId64, + at->value.as_int); + break; + case slogAnyFloat: + buf_appendf(line, sizeof line, &i, "%.15g", + at->value.as_float); + break; + case slogAnyString: + buf_appendf(line, sizeof line, &i, "%s", + at->value.as_str ? at->value.as_str : ""); + break; + case slogAnyBool: + buf_appendf(line, sizeof line, &i, "%s", + at->value.as_bool ? "true" : "false"); + break; + case slogAnyTime: { + char tb[64]; + slogFormatTimespec(tb, sizeof tb, &at->value.as_time); + buf_appendf(line, sizeof line, &i, "%s", tb); + break; + } + case slogAnyPtr: + buf_appendf(line, sizeof line, &i, "%p", + at->value.as_ptr); + break; + case slogAnyBytes: { + buf_appendf(line, sizeof line, &i, "0x"); + const unsigned char *p = + (const unsigned char *)at->value.as_bytes.data; + for (size_t j = 0; j < at->value.as_bytes.size; ++j) + buf_appendf(line, sizeof line, &i, "%02x", + p[j]); + break; + } + default: + buf_appendf(line, sizeof line, &i, ""); + break; + } + } + + buf_appendf(line, sizeof line, &i, "\n"); + fputs(line, out); + fflush(out); +} + +#ifndef SLOG_FUNC +#if defined(__cplusplus) || \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) +#define SLOG_FUNC __func__ +#elif defined(_MSC_VER) +#define SLOG_FUNC __FUNCTION__ +#else +#define SLOG_FUNC "unknown" +#endif +#endif + +#if !defined(__cplusplus) +#define SLOG_SOURCE ((SlogSource){ __FILE__, SLOG_FUNC, __LINE__ }) +#else +#define SLOG_SOURCE_MAKE(name) \ + SlogSource name = { __FILE__, SLOG_FUNC, __LINE__ } +#endif + +#define SLOG_CALL(LOGGER_PTR, LEVEL, MSG, ATTRS, COUNT, SRCPTR) \ + do { \ + (LOGGER_PTR) \ + ->Handler((LOGGER_PTR), (LEVEL), (MSG), (ATTRS), \ + (COUNT), (SRCPTR)); \ + } while (0) + +#define SLOG_ATTR_COUNT(ATTRS) \ + ((ATTRS) == NULL ? 0 : sizeof(ATTRS) / sizeof((ATTRS)[0])) + +#define SLOG_DEBUG(LOGGER_PTR, MSG, ATTRS) \ + SLOG_CALL((LOGGER_PTR), SlogLevelDebug, (MSG), (ATTRS), \ + (SLOG_ATTR_COUNT(ATTRS)), (&SLOG_SOURCE)) + +#define SLOG_INFO(LOGGER_PTR, MSG, ATTRS) \ + SLOG_CALL((LOGGER_PTR), SlogLevelInfo, (MSG), (ATTRS), \ + (SLOG_ATTR_COUNT(ATTRS)), (&SLOG_SOURCE)) + +#define SLOG_WARN(LOGGER_PTR, MSG, ATTRS) \ + SLOG_CALL((LOGGER_PTR), SlogLevelWarn, (MSG), (ATTRS), \ + (SLOG_ATTR_COUNT(ATTRS)), (&SLOG_SOURCE)) + +#define SLOG_ERROR(LOGGER_PTR, MSG, ATTRS) \ + SLOG_CALL((LOGGER_PTR), SlogLevelError, (MSG), (ATTRS), \ + (SLOG_ATTR_COUNT(ATTRS)), (&SLOG_SOURCE)) + +#define SLOG_DEFAULT_TEXT_LOGGER \ + (SlogLogger) \ + { \ + .Handler = SlogTextHandler, .Opts = &(SlogHandlerOpts) \ + { \ + .File = stdout, .Prefix = NULL, .AddSource = false, \ + .Json = false \ + } \ + } + +#define SLOG_DEFAULT_JSON_LOGGER \ + (SlogLogger) \ + { \ + .Handler = SlogTextHandler, .Opts = &(SlogHandlerOpts) \ + { \ + .File = stdout, .Prefix = NULL, .AddSource = true, \ + .Json = true \ + } \ + } + +#ifdef __cplusplus +} +#endif + +#endif // LOGGER_H diff --git a/tests/slog.c b/tests/slog.c new file mode 100644 index 0000000..65c2a37 --- /dev/null +++ b/tests/slog.c @@ -0,0 +1,40 @@ +#include +#include + +int main(void) +{ + struct SlogHandlerOpts opts1 = { + .File = stdout, + .MinLevel = SlogLevelDebug, + .Prefix = NULL, + .AddSource = true, + .Json = false, + }; + + struct SlogLogger l1 = { + SlogTextHandler, + &opts1, + }; + + struct SlogHandlerOpts opts2 = { + .File = stderr, + .MinLevel = SlogLevelError, + .Prefix = "scope", + .AddSource = true, + .Json = true, + }; + + struct SlogLogger l2 = { + SlogTextHandler, + &opts2, + }; + + struct SlogAttr attrs[] = { + SlogAttrB("ok", true), + }; + + SLOG_INFO(&l1, "hello", attrs); + SLOG_ERROR(&l2, "hello", attrs); + + return 0; +}