diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5172429 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..f28a66d --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,129 @@ +name: Main +on: + push: + branches: [ "main" ] + tags: + - 'v*' + pull_request: + branches: [ "main" ] +jobs: + ubuntu-build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Restore + run: dotnet restore src + - name: Build + run: dotnet build src --no-restore --configuration Release + - name: Test + run: dotnet test src --no-build --configuration Release --verbosity normal + - name: Publish + run: dotnet publish src/Analog --configuration Release -p:PublishSingleFile=true --self-contained + - name: Zip + run: zip /home/runner/work/analog/analog-linux-x64 /home/runner/work/analog/analog/src/Analog/bin/Release/net8.0/linux-x64/publish/* + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: analog-linux-x64 + path: /home/runner/work/analog/analog-linux-x64.zip + windows-build: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Restore + run: dotnet restore src + - name: Build + run: dotnet build src --no-restore --configuration Release + - name: Test + run: dotnet test src --no-build --configuration Release --verbosity normal + - name: Publish + run: dotnet publish src/Analog --configuration Release -p:PublishSingleFile=true --self-contained + - name: Zip + shell: pwsh + run: Compress-Archive -Path D:\a\analog\analog\src\Analog\bin\Release\net8.0\win-x64\publish\* -DestinationPath D:\a\analog\analog-win-x64.zip + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: analog-win-x64 + path: D:\a\analog\analog-win-x64.zip + macos-build: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Restore + run: dotnet restore src + - name: Build + run: dotnet build src --no-restore --configuration Release + - name: Test + run: dotnet test src --no-build --configuration Release --verbosity normal + - name: Publish + run: dotnet publish src/Analog --configuration Release -p:PublishSingleFile=true --self-contained + - name: Zip + run: zip /Users/runner/work/analog/analog-osx-arm64 /Users/runner/work/analog/analog/src/Analog/bin/Release/net8.0/osx-arm64/publish/* + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: analog-osx-arm64 + path: /Users/runner/work/analog/analog-osx-arm64.zip + release: + runs-on: ubuntu-latest + if: startsWith(github.event.ref, 'refs/tags/v') + needs: [ubuntu-build, windows-build, macos-build] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + - run: ls ${{ github.workspace }}/analog-osx-x64 + - name: Create New Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Analog ${{ github.ref }} + draft: false + prerelease: false + - name: Upload MacOS Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ github.workspace }}/analog-osx-arm64/analog-osx-arm64.zip + asset_name: analog-osx-arm64.zip + asset_content_type: application/zip + - name: Upload Linux Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ github.workspace }}/analog-linux-x64/analog-linux-x64.zip + asset_name: analog-linux-x64.zip + asset_content_type: application/zip + - name: Upload Windows Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ github.workspace }}/analog-win-x64/analog-win-x64.zip + asset_name: analog-win-x64.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6a47aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode/ +.vs/ +.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c4fe64 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Analog + +A powerful tool for quick and efficient log file analysis, offering robust filtering, searching, and visualization capabilities. + +## Getting Started + +Parse log file using the default template: +```bash +analog -f /path/to/logfile.log +``` + +Parse log file using a custom template: +```bash +analog -f /path/to/logfile.log -t custom_template_name +``` + +## Features +- [x] **Custom Templates**: Create custom templates to parse and analyze log files. +- [ ] **Visualizations**: Generate visualizations to better understand log data. +- [ ] **Filtering**: Filter log data based on specific criteria. \ No newline at end of file diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 0000000..b5f39e6 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,364 @@ +root = true + +# All files +[*] +indent_style = space + +# Xml files +[*.xml] +indent_size = 2 + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### +[*.{cs,vb}] + +# Organize usings +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = true +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:warning + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# Coding Conventions #### +[*.cs] + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### +[*.{cs,vb}] + +# Naming rules + +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion +dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces +dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase + +dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion +dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters +dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase + +dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods +dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties +dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.events_should_be_pascalcase.symbols = events +dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables +dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase + +dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants +dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase + +dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion +dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters +dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase + +dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields +dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion +dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields +dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase + +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase + +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums +dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase + +# Symbol specifications + +dotnet_naming_symbols.interfaces.applicable_kinds = interface +dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interfaces.required_modifiers = + +dotnet_naming_symbols.enums.applicable_kinds = enum +dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.enums.required_modifiers = + +dotnet_naming_symbols.events.applicable_kinds = event +dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.events.required_modifiers = + +dotnet_naming_symbols.methods.applicable_kinds = method +dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.methods.required_modifiers = + +dotnet_naming_symbols.properties.applicable_kinds = property +dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.properties.required_modifiers = + +dotnet_naming_symbols.public_fields.applicable_kinds = field +dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_fields.required_modifiers = + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_fields.required_modifiers = + +dotnet_naming_symbols.private_static_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_static_fields.required_modifiers = static + +dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum +dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types_and_namespaces.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.type_parameters.applicable_kinds = namespace +dotnet_naming_symbols.type_parameters.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters.required_modifiers = + +dotnet_naming_symbols.private_constant_fields.applicable_kinds = field +dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_constant_fields.required_modifiers = const + +dotnet_naming_symbols.local_variables.applicable_kinds = local +dotnet_naming_symbols.local_variables.applicable_accessibilities = local +dotnet_naming_symbols.local_variables.required_modifiers = + +dotnet_naming_symbols.local_constants.applicable_kinds = local +dotnet_naming_symbols.local_constants.applicable_accessibilities = local +dotnet_naming_symbols.local_constants.required_modifiers = const + +dotnet_naming_symbols.parameters.applicable_kinds = parameter +dotnet_naming_symbols.parameters.applicable_accessibilities = * +dotnet_naming_symbols.parameters.required_modifiers = + +dotnet_naming_symbols.public_constant_fields.applicable_kinds = field +dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_constant_fields.required_modifiers = const + +dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function +dotnet_naming_symbols.local_functions.applicable_accessibilities = * +dotnet_naming_symbols.local_functions.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascalcase.required_prefix = +dotnet_naming_style.pascalcase.required_suffix = +dotnet_naming_style.pascalcase.word_separator = +dotnet_naming_style.pascalcase.capitalization = pascal_case + +dotnet_naming_style.ipascalcase.required_prefix = I +dotnet_naming_style.ipascalcase.required_suffix = +dotnet_naming_style.ipascalcase.word_separator = +dotnet_naming_style.ipascalcase.capitalization = pascal_case + +dotnet_naming_style.tpascalcase.required_prefix = T +dotnet_naming_style.tpascalcase.required_suffix = +dotnet_naming_style.tpascalcase.word_separator = +dotnet_naming_style.tpascalcase.capitalization = pascal_case + +dotnet_naming_style._camelcase.required_prefix = _ +dotnet_naming_style._camelcase.required_suffix = +dotnet_naming_style._camelcase.word_separator = +dotnet_naming_style._camelcase.capitalization = camel_case + +dotnet_naming_style.camelcase.required_prefix = +dotnet_naming_style.camelcase.required_suffix = +dotnet_naming_style.camelcase.word_separator = +dotnet_naming_style.camelcase.capitalization = camel_case + +dotnet_naming_style.s_camelcase.required_prefix = s_ +dotnet_naming_style.s_camelcase.required_suffix = +dotnet_naming_style.s_camelcase.word_separator = +dotnet_naming_style.s_camelcase.capitalization = camel_case + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..104b544 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/src/Analog.Tests/Analog.Tests.fsproj b/src/Analog.Tests/Analog.Tests.fsproj new file mode 100644 index 0000000..ccaa5ae --- /dev/null +++ b/src/Analog.Tests/Analog.Tests.fsproj @@ -0,0 +1,37 @@ + + + + net8.0 + + false + false + true + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/Analog.Tests/ParserTest.fs b/src/Analog.Tests/ParserTest.fs new file mode 100644 index 0000000..22609ff --- /dev/null +++ b/src/Analog.Tests/ParserTest.fs @@ -0,0 +1,80 @@ +module Analog.Tests.ParserTest + +open System.IO +open System.Text +open Xunit +open Analog.Parser + +let pattern = + @"^\[(?[\d\-]{10} [\d\:\.\+\ ]{19})?\] \[(?[A-Z]{3})?\] (?[\s\S]*?\n*(?=^\[[\d\-]{10}.*?(?:[^ \n]+ )|\z))" + +let bytes = + """ +[2018-10-15 15:38:22.685 +02:00] [INF] Configuration Result: +[Success] Name GTS.MAUTO.Service +[Success] DisplayName GTS MAUTO service +[Success] Description Service for standalone mode linked to GTS Vision application. +[Success] ServiceName GTS.MAUTO.Service +[2018-10-15 15:38:22.729 +02:00] [INF] Topshelf v4.0.0.0, .NET Framework v4.0.30319.42000 +[2018-10-15 15:38:22.829 +02:00] [INF] Using default implementation for object serializer +[2018-10-15 15:38:22.845 +02:00] [INF] Using default implementation for ThreadExecutor +[2018-10-15 15:38:22.860 +02:00] [INF] Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl +[2018-10-15 15:38:22.860 +02:00] [INF] Quartz Scheduler v.2.6.1.0 created. +[2018-10-15 15:38:22.861 +02:00] [INF] JobFactory set to: GTS.Scheduler.AutofacJobFactory +[2018-10-15 15:38:22.861 +02:00] [INF] RAMJobStore initialized. +[2018-10-15 15:38:22.863 +02:00] [INF] Scheduler meta-data: Quartz Scheduler (v2.6.1.0) 'QuartzScheduler' with instanceId 'NON_CLUSTERED' + Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally. + NOT STARTED. + Currently in standby mode. + Number of jobs executed: 0 + Using thread pool 'Quartz.Simpl.SimpleThreadPool' - with 1 threads. + Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered. +[2018-10-15 15:38:22.863 +02:00] [INF] Quartz scheduler 'QuartzScheduler' initialized +[2018-10-15 15:38:22.863 +02:00] [INF] Quartz scheduler version: 2.6.1.0 +[2018-10-15 15:38:22.888 +02:00] [DBG] Started by the Windows services process +[2018-10-15 15:38:22.888 +02:00] [DBG] Running as a service, creating service host. +[2018-10-15 15:38:22.889 +02:00] [INF] Starting as a Windows service +[2018-10-15 15:38:22.891 +02:00] [DBG] [Topshelf] Starting up as a windows service application +[2018-10-15 15:38:22.893 +02:00] [INF] [Topshelf] Starting +[2018-10-15 15:38:22.893 +02:00] [DBG] [Topshelf] Current Directory: D:\GTS_VISION\MAUTO Service +[2018-10-15 15:38:22.893 +02:00] [DBG] [Topshelf] Arguments: +[2018-10-15 15:38:22.895 +02:00] [INF] MAUTO Windows Service starting +[2018-10-15 15:38:22.994 +02:00] [DBG] Connected to Data Source=localhost;Initial Catalog=IRECVISION_MQB_PROD_LOCAL;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +[2018-10-15 15:38:23.032 +02:00] [DBG] Connected to Data Source=GTSSQL-VISION;Initial Catalog=IRECVISION_MQB_PROD;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +[2018-10-15 15:38:23.033 +02:00] [DBG] Connected to Data Source=localhost;Initial Catalog=IRECVISION_MQB_PROD_LOCAL;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +[2018-10-15 15:38:23.038 +02:00] [DBG] Connected to Data Source=GTSSQL-VISION;Initial Catalog=IRECVISION_MQB_PROD;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +[2018-10-15 15:38:23.040 +02:00] [DBG] Connected to Data Source=localhost;Initial Catalog=IRECVISION_MQB_PROD_LOCAL;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +[2018-10-15 15:38:23.040 +02:00] [DBG] Get MAUTOParameter +SELECT + [Mp_jetonmaitre] AS [RemoteToken], + [Mp_jetonlocal] AS [LocalToken] +FROM [dbo].[MAUTOParametre] +[2018-10-15 15:38:23.162 +02:00] [ERR] Failed to start service +System.Exception: Integrity error, the token of the local base does not correspond to the remote database. + à GTS.MAUTO.MAUTOClient.CheckIntegrity() dans D:\TFS\Beta\Quattro.2.x\Quattro.2.28\Quattro.2.28.0\MAUTO\src\GTS.MAUTO\MAUTOClient.cs:ligne 122 + à GTS.MAUTO.Service.MAUTOService.Start() dans D:\TFS\Beta\Quattro.2.x\Quattro.2.28\Quattro.2.28.0\MAUTO\src\GTS.MAUTO.Service\MAUTOService.cs:ligne 44 +[2018-10-15 15:38:23.184 +02:00] [INF] [Topshelf] Started +[2018-10-15 15:40:55.598 +02:00] [INF] [Topshelf] Stopping +[2018-10-15 15:40:55.599 +02:00] [INF] MAUTO Windows Service stopping +[2018-10-15 15:40:55.599 +02:00] [INF] Cron scheduler stopping +[2018-10-15 15:40:55.601 +02:00] [INF] Scheduler QuartzScheduler_$_NON_CLUSTERED shutting down. +[2018-10-15 15:40:55.601 +02:00] [INF] Scheduler QuartzScheduler_$_NON_CLUSTERED paused. +[2018-10-15 15:40:55.604 +02:00] [DBG] Shutting down threadpool... +[2018-10-15 15:40:55.604 +02:00] [DBG] Shutdown of threadpool complete. +[2018-10-15 15:40:55.604 +02:00] [INF] Scheduler QuartzScheduler_$_NON_CLUSTERED Shutdown complete. +[2018-10-15 15:40:55.604 +02:00] [INF] Cron scheduler stopped +[2018-10-15 15:40:55.604 +02:00] [INF] MAUTO Windows Service stopped +[2018-10-15 15:40:55.606 +02:00] [INF] Cron scheduler stopping +[2018-10-15 15:40:55.606 +02:00] [INF] Cron scheduler stopped +[2018-10-15 15:40:55.660 +02:00] [INF] [Topshelf] Stopped +[2018-10-15 15:40:55.953 +02:00] [DBG] WorkerThread is shut down +""" + |> Encoding.UTF8.GetBytes + +[] +let ``parse iterates through all log entries`` () = + use stream = new MemoryStream(bytes) + let result = System.Collections.Generic.List>() + let tap (log: Map) = result.Add(log) + parse tap pattern stream + Snapshot.compare result diff --git a/src/Analog.Tests/Program.fs b/src/Analog.Tests/Program.fs new file mode 100644 index 0000000..0695f84 --- /dev/null +++ b/src/Analog.Tests/Program.fs @@ -0,0 +1 @@ +module Program = let [] main _ = 0 diff --git a/src/Analog.Tests/Snapshot.fs b/src/Analog.Tests/Snapshot.fs new file mode 100644 index 0000000..c36da61 --- /dev/null +++ b/src/Analog.Tests/Snapshot.fs @@ -0,0 +1,10 @@ +module Analog.Tests.Snapshot + +open System.Threading.Tasks +open VerifyTests +open VerifyXunit + +let compare (data: obj) : Task = + let settings = VerifySettings() + settings.UseDirectory "snap" + Verifier.Verify(data, settings).ToTask() diff --git a/src/Analog.Tests/TemplateTest.fs b/src/Analog.Tests/TemplateTest.fs new file mode 100644 index 0000000..845c1ae --- /dev/null +++ b/src/Analog.Tests/TemplateTest.fs @@ -0,0 +1,8 @@ +module Analog.Tests.TemplateTest + +open Xunit +open FsUnit.Xunit +open Analog.Template + +[] +let ``map returns a non-empty map`` () = configuration |> should not' <| be Empty diff --git a/src/Analog.Tests/snap/ParserTest.parse iterates through all log entries.verified.txt b/src/Analog.Tests/snap/ParserTest.parse iterates through all log entries.verified.txt new file mode 100644 index 0000000..fc7744d --- /dev/null +++ b/src/Analog.Tests/snap/ParserTest.parse iterates through all log entries.verified.txt @@ -0,0 +1,306 @@ +[ + { + Message: +Configuration Result: +[Success] Name GTS.MAUTO.Service +[Success] DisplayName GTS MAUTO service +[Success] Description Service for standalone mode linked to GTS Vision application. +[Success] ServiceName GTS.MAUTO.Service +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.685 +02:00 + }, + { + Message: +Topshelf v4.0.0.0, .NET Framework v4.0.30319.42000 +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.729 +02:00 + }, + { + Message: +Using default implementation for object serializer +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.829 +02:00 + }, + { + Message: +Using default implementation for ThreadExecutor +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.845 +02:00 + }, + { + Message: +Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.860 +02:00 + }, + { + Message: +Quartz Scheduler v.2.6.1.0 created. +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.860 +02:00 + }, + { + Message: +JobFactory set to: GTS.Scheduler.AutofacJobFactory +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.861 +02:00 + }, + { + Message: +RAMJobStore initialized. +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.861 +02:00 + }, + { + Message: +Scheduler meta-data: Quartz Scheduler (v2.6.1.0) 'QuartzScheduler' with instanceId 'NON_CLUSTERED' + Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally. + NOT STARTED. + Currently in standby mode. + Number of jobs executed: 0 + Using thread pool 'Quartz.Simpl.SimpleThreadPool' - with 1 threads. + Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered. +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.863 +02:00 + }, + { + Message: +Quartz scheduler 'QuartzScheduler' initialized +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.863 +02:00 + }, + { + Message: +Quartz scheduler version: 2.6.1.0 +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.863 +02:00 + }, + { + Message: +Started by the Windows services process +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:22.888 +02:00 + }, + { + Message: +Running as a service, creating service host. +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:22.888 +02:00 + }, + { + Message: +Starting as a Windows service +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.889 +02:00 + }, + { + Message: +[Topshelf] Starting up as a windows service application +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:22.891 +02:00 + }, + { + Message: +[Topshelf] Starting +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.893 +02:00 + }, + { + Message: +[Topshelf] Current Directory: D:\GTS_VISION\MAUTO Service +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:22.893 +02:00 + }, + { + Message: +[Topshelf] Arguments: +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:22.893 +02:00 + }, + { + Message: +MAUTO Windows Service starting +, + Severity: INF, + Timestamp: 2018-10-15 15:38:22.895 +02:00 + }, + { + Message: +Connected to Data Source=localhost;Initial Catalog=IRECVISION_MQB_PROD_LOCAL;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:22.994 +02:00 + }, + { + Message: +Connected to Data Source=GTSSQL-VISION;Initial Catalog=IRECVISION_MQB_PROD;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:23.032 +02:00 + }, + { + Message: +Connected to Data Source=localhost;Initial Catalog=IRECVISION_MQB_PROD_LOCAL;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:23.033 +02:00 + }, + { + Message: +Connected to Data Source=GTSSQL-VISION;Initial Catalog=IRECVISION_MQB_PROD;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:23.038 +02:00 + }, + { + Message: +Connected to Data Source=localhost;Initial Catalog=IRECVISION_MQB_PROD_LOCAL;Integrated Security=False;User ID=GTSMAUTO;Password=***;MultipleActiveResultSets=False +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:23.040 +02:00 + }, + { + Message: +Get MAUTOParameter +SELECT + [Mp_jetonmaitre] AS [RemoteToken], + [Mp_jetonlocal] AS [LocalToken] +FROM [dbo].[MAUTOParametre] +, + Severity: DBG, + Timestamp: 2018-10-15 15:38:23.040 +02:00 + }, + { + Message: +Failed to start service +System.Exception: Integrity error, the token of the local base does not correspond to the remote database. + à GTS.MAUTO.MAUTOClient.CheckIntegrity() dans D:\TFS\Beta\Quattro.2.x\Quattro.2.28\Quattro.2.28.0\MAUTO\src\GTS.MAUTO\MAUTOClient.cs:ligne 122 + à GTS.MAUTO.Service.MAUTOService.Start() dans D:\TFS\Beta\Quattro.2.x\Quattro.2.28\Quattro.2.28.0\MAUTO\src\GTS.MAUTO.Service\MAUTOService.cs:ligne 44 +, + Severity: ERR, + Timestamp: 2018-10-15 15:38:23.162 +02:00 + }, + { + Message: +[Topshelf] Started +, + Severity: INF, + Timestamp: 2018-10-15 15:38:23.184 +02:00 + }, + { + Message: +[Topshelf] Stopping +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.598 +02:00 + }, + { + Message: +MAUTO Windows Service stopping +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.599 +02:00 + }, + { + Message: +Cron scheduler stopping +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.599 +02:00 + }, + { + Message: +Scheduler QuartzScheduler_$_NON_CLUSTERED shutting down. +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.601 +02:00 + }, + { + Message: +Scheduler QuartzScheduler_$_NON_CLUSTERED paused. +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.601 +02:00 + }, + { + Message: +Shutting down threadpool... +, + Severity: DBG, + Timestamp: 2018-10-15 15:40:55.604 +02:00 + }, + { + Message: +Shutdown of threadpool complete. +, + Severity: DBG, + Timestamp: 2018-10-15 15:40:55.604 +02:00 + }, + { + Message: +Scheduler QuartzScheduler_$_NON_CLUSTERED Shutdown complete. +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.604 +02:00 + }, + { + Message: +Cron scheduler stopped +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.604 +02:00 + }, + { + Message: +MAUTO Windows Service stopped +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.604 +02:00 + }, + { + Message: +Cron scheduler stopping +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.606 +02:00 + }, + { + Message: +Cron scheduler stopped +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.606 +02:00 + }, + { + Message: +[Topshelf] Stopped +, + Severity: INF, + Timestamp: 2018-10-15 15:40:55.660 +02:00 + }, + { + Message: +WorkerThread is shut down +, + Severity: DBG, + Timestamp: 2018-10-15 15:40:55.953 +02:00 + } +] \ No newline at end of file diff --git a/src/Analog/Analog.fsproj b/src/Analog/Analog.fsproj new file mode 100644 index 0000000..a2472b7 --- /dev/null +++ b/src/Analog/Analog.fsproj @@ -0,0 +1,23 @@ + + + + net8.0 + Exe + + + + + + + + + + + + + + + + + + diff --git a/src/Analog/Parser.fs b/src/Analog/Parser.fs new file mode 100644 index 0000000..ff0ad67 --- /dev/null +++ b/src/Analog/Parser.fs @@ -0,0 +1,55 @@ +module Analog.Parser + +open System +open System.Linq +open System.IO +open System.Text.RegularExpressions + +/// +/// Parses the given stream using the provided regular expression and applies a function to each match. +/// This function reads the stream in blocks, matches each block against the regex, +/// and applies the function to each match. It handles partial matches that span across block boundaries. +/// +/// +/// A function to apply to each map of named groups found in matches. +/// The map represents the named groups captured by the regex in a single match. +/// +/// The regular expression used to find matches in the stream. +/// The stream to be parsed. +let parse (tap: Map -> unit) (pattern: string) (stream: Stream) = + use reader = new StreamReader(stream) + + let regex = + Regex(pattern, RegexOptions.Multiline ||| RegexOptions.Compiled, TimeSpan.FromSeconds(5)) + + let regexTuple (regexGroup: Group) = regexGroup.Name, regexGroup.Value + + let regexTuples regexGroups = regexGroups |> Seq.map regexTuple + + let regexGroups (regexMatch: Match) = regexMatch.Groups |> Seq.skip 1 + + let regexMap regexMatch = + regexMatch |> regexGroups |> regexTuples |> Map.ofSeq + + let regexLeftover (regexInput: string) (regexMatches: Match seq) = + match regexMatches |> Seq.tryLast with + | Some value -> regexInput[value.Index ..] + | None -> regexInput + + let assignedChar char = char <> Unchecked.defaultof + + let assignedChars chars = chars |> Array.filter assignedChar + + let rec nextBlock (leftover: string) = + let buffer = Array.zeroCreate 1024 + let block = reader.ReadBlock(buffer, 0, buffer.Length) + let input = buffer |> assignedChars |> String |> (fun current -> leftover + current) + let matches = regex.Matches input + matches.SkipLast 1 |> Seq.map regexMap |> Seq.iter tap + + if block < buffer.Length then + regex.Matches(regexLeftover input matches) |> Seq.map regexMap |> Seq.iter tap + else + nextBlock (regexLeftover input matches) + + nextBlock String.Empty diff --git a/src/Analog/Program.fs b/src/Analog/Program.fs new file mode 100644 index 0000000..293a27f --- /dev/null +++ b/src/Analog/Program.fs @@ -0,0 +1,35 @@ +open System.IO +open System.Text.Json +open Analog +open Argu +open Spectre.Console +open Spectre.Console.Json + +type Argument = + | [] File of string + | [] Template of string + + interface IArgParserTemplate with + member this.Usage = + match this with + | File _ -> "log file path." + | Template _ -> "log file template." + +[] +let main argv = + try + let command = ArgumentParser.Create().ParseCommandLine(argv) + + let template = + command.TryGetResult Template + |> Option.map (fun template -> Template.configuration |> Map.find template) + |> Option.defaultValue (Template.configuration |> Seq.head |> _.Value) + + let render = JsonSerializer.Serialize >> JsonText >> AnsiConsole.Write + let parse = Parser.parse render template.Pattern + command.GetResults File |> Seq.iter (fun file -> File.OpenRead file |> parse) + command |> ignore + 0 + with :? ArguParseException as e -> + eprintfn $"%s{e.Message}" + 1 diff --git a/src/Analog/Template.fs b/src/Analog/Template.fs new file mode 100644 index 0000000..4c780c0 --- /dev/null +++ b/src/Analog/Template.fs @@ -0,0 +1,17 @@ +module Analog.Template + +open System.IO +open System.Collections.Generic +open YamlDotNet.Serialization +open YamlDotNet.Serialization.NamingConventions + +[] +type Configuration = { Pattern: string } + +let configuration = + DeserializerBuilder() + |> _.WithNamingConvention(CamelCaseNamingConvention.Instance) + |> _.Build() + |> _.Deserialize>(File.ReadAllText("template.yml")) + |> Seq.map (fun template -> template.Key, template.Value) + |> Map.ofSeq diff --git a/src/Analog/template.json b/src/Analog/template.json new file mode 100644 index 0000000..83949ce --- /dev/null +++ b/src/Analog/template.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Analog Template", + "type": "object", + "patternProperties": { + ".*": { + "type": "object", + "properties": { + "pattern": { + "type": "string" + } + }, + "required": ["pattern"] + } + } +} diff --git a/src/Analog/template.yml b/src/Analog/template.yml new file mode 100644 index 0000000..0ba7fd3 --- /dev/null +++ b/src/Analog/template.yml @@ -0,0 +1,4 @@ +# yaml-language-server: $schema=template.json + +default: + pattern: '^\[(?[\d\-]{10} [\d\:\.\+\ ]{19})?\] \[(?[A-Z]{3})?\] (?[\s\S]*?\n*(?=^\[[\d\-]{10}.*?(?:[^ \n]+ )|\z))' \ No newline at end of file diff --git a/src/analog.sln b/src/analog.sln new file mode 100644 index 0000000..e364731 --- /dev/null +++ b/src/analog.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Analog", "Analog\Analog.fsproj", "{7A9C59B7-C5F5-449F-A446-DD2703323686}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Analog.Tests", "Analog.Tests\Analog.Tests.fsproj", "{D742D202-8FE7-42B8-B728-ACF3C3B28880}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|arm64 = Debug|arm64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|arm64 = Release|arm64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Debug|arm64.ActiveCfg = Debug|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Debug|arm64.Build.0 = Debug|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Debug|x86.Build.0 = Debug|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Release|Any CPU.Build.0 = Release|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Release|arm64.ActiveCfg = Release|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Release|arm64.Build.0 = Release|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Release|x86.ActiveCfg = Release|Any CPU + {7A9C59B7-C5F5-449F-A446-DD2703323686}.Release|x86.Build.0 = Release|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Debug|arm64.ActiveCfg = Debug|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Debug|arm64.Build.0 = Debug|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Debug|x86.ActiveCfg = Debug|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Debug|x86.Build.0 = Debug|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Release|Any CPU.Build.0 = Release|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Release|arm64.ActiveCfg = Release|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Release|arm64.Build.0 = Release|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Release|x86.ActiveCfg = Release|Any CPU + {D742D202-8FE7-42B8-B728-ACF3C3B28880}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {06FB59AB-911B-498D-89BF-3DF9DA28C2E7} + EndGlobalSection +EndGlobal