├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── changelog-release.yml │ ├── changelog-update.yml │ ├── dependabot-auto-merge.yml │ ├── docs.yml │ ├── pull-request-description-check.yml │ ├── release-description-update.yml │ ├── static-analyze.yml │ └── tests.yml ├── .gitignore ├── .php-cs-fixer.php ├── AppInsightsPHPBundle.php ├── CHANGELOG.md ├── Cache └── NullCache.php ├── Command ├── TrackDependencyCommand.php ├── TrackEventCommand.php ├── TrackExceptionCommand.php └── TrackMetricCommand.php ├── DependencyInjection ├── AppInsightsPHPExtension.php ├── Compiler │ └── DoctrineDependencyPassPass.php └── Configuration.php ├── FlatArray.php ├── LICENSE ├── Listener ├── ExceptionListener.php ├── HttpRequestListener.php └── KernelTerminateListener.php ├── README.md ├── Resources ├── config │ ├── app_insights_php.php │ ├── app_insights_php_console.php │ ├── app_insights_php_doctrine.php │ ├── app_insights_php_monolog.php │ ├── app_insights_php_symfony.php │ └── app_insights_php_twig.php └── doc │ ├── dependencies.md │ ├── how_it_works.md │ ├── index.md │ ├── page_views.md │ └── traces.md ├── Tests ├── Command │ ├── TrackDependencyCommandTest.php │ ├── TrackEventCommandTest.php │ ├── TrackExceptionCommandTest.php │ └── TrackMetricCommandTest.php ├── DependencyInjection │ ├── AppInsightsPHPExtensionTest.php │ └── ConfigurationTest.php ├── FlatArrayTest.php ├── Functional │ ├── AppKernel.php │ ├── README.md │ ├── bin │ │ └── console │ └── config │ │ └── app_insights_php.php.dist ├── Listener │ └── KernelTerminateListenerTest.php └── Twig │ └── TelemetryExtensionTest.php ├── Twig └── TelemetryExtension.php ├── composer.json ├── composer.lock ├── phpunit.xml.dist └── shell.nix /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 |

Change Log

3 |
4 |

Added

5 | 8 |

Fixed

9 | 12 |

Changed

13 | 16 |

Removed

17 | 20 |

Deprecated

21 | 24 |

Security

25 | 28 |
29 |
30 | 31 |

Description

32 | 33 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "composer" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/workflows/changelog-release.yml: -------------------------------------------------------------------------------- 1 | name: "Changelog - Release Unreleased" 2 | 3 | ################################################################### 4 | # WARNING - THIS ACTION WILL PUSH COMMIT DIRECTLY INTO REPOSITORY # 5 | ################################################################### 6 | 7 | on: 8 | push: 9 | tags: 10 | - '*' 11 | 12 | jobs: 13 | changelog-release-unreleased: 14 | name: "Update Changelog - Release Unreleased" 15 | 16 | runs-on: "ubuntu-latest" 17 | 18 | steps: 19 | - id: "tag-name" 20 | run: | 21 | tag=$(echo ${{ github.event.ref }} | cut -c11-) 22 | echo "::set-output name=tag::$tag" 23 | - name: "Update CHANGELOG" 24 | uses: "docker://aeonphp/automation:latest" 25 | env: 26 | AEON_AUTOMATION_GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 27 | with: 28 | entrypoint: "/composer/vendor/bin/automation" 29 | args: "changelog:release:unreleased ${{ github.repository }} CHANGELOG.md ${{ steps.tag-name.outputs.tag }} --github-file-changelog-update" 30 | -------------------------------------------------------------------------------- /.github/workflows/changelog-update.yml: -------------------------------------------------------------------------------- 1 | name: "Changelog - Update Unreleased" 2 | 3 | ################################################################### 4 | # WARNING - THIS ACTION WILL PUSH COMMIT DIRECTLY INTO REPOSITORY # 5 | ################################################################### 6 | 7 | on: 8 | push: 9 | branches: 10 | - 1.x 11 | 12 | jobs: 13 | changelog-update-unreleased: 14 | name: "Changelog - Update Unreleased" 15 | 16 | runs-on: "ubuntu-latest" 17 | 18 | steps: 19 | - name: "Checkout" 20 | uses: "actions/checkout@v2" 21 | 22 | - name: "Restore Automation cache" 23 | uses: "actions/cache@v2" 24 | with: 25 | path: | 26 | cache 27 | key: "${{ runner.os }}-automation-${{ hashFiles('**/CHANGELOG.md') }}" 28 | restore-keys: | 29 | ${{ runner.os }}-automation- 30 | 31 | - name: "Update CHANGELOG" 32 | uses: "docker://aeonphp/automation:latest" 33 | env: 34 | AEON_AUTOMATION_GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 35 | EON_AUTOMATION_CACHE_DIR: "/github/workspace/cache" 36 | with: 37 | entrypoint: "/composer/vendor/bin/automation" 38 | args: "changelog:generate ${{ github.repository }} --github-file-update-path=CHANGELOG.md --skip-from=\"dependabot[bot]\" --skip-from=\"aeon-automation\"" 39 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot - Auto Merge 2 | on: pull_request 3 | 4 | permissions: 5 | contents: write 6 | pull-requests: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | - name: Dependabot metadata 14 | id: metadata 15 | uses: dependabot/fetch-metadata@v1 16 | with: 17 | github-token: "${{ secrets.GITHUB_TOKEN }}" 18 | - name: Enable auto-merge for Dependabot PRs 19 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 20 | run: gh pr merge --auto --merge "$PR_URL" 21 | env: 22 | PR_URL: ${{github.event.pull_request.html_url}} 23 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: "Documentation Linter" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - "1.x" 8 | 9 | jobs: 10 | documentation-linter: 11 | name: "Documentation Linter" 12 | 13 | runs-on: "ubuntu-latest" 14 | 15 | steps: 16 | - name: "Checkout" 17 | uses: "actions/checkout@v2" 18 | 19 | - name: "MD Link Linter" 20 | uses: "docker://norberttech/md-link-linter:latest" 21 | with: 22 | entrypoint: "/composer/vendor/bin/mdlinklint" 23 | args: "--exclude=vendor ." 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-description-check.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Description Check" 2 | 3 | on: 4 | pull_request: 5 | types: ["opened", "edited", "reopened", "ready_for_review"] 6 | 7 | jobs: 8 | pull-request-description-check: 9 | name: "Pull Request Description" 10 | 11 | runs-on: "ubuntu-latest" 12 | 13 | steps: 14 | - name: "Pull Request Description - Check" 15 | uses: "docker://aeonphp/automation:latest" 16 | env: 17 | AEON_AUTOMATION_GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 18 | with: 19 | entrypoint: "/composer/vendor/bin/automation" 20 | args: "pull-request:description:check aeon-php/automation ${{ github.event.pull_request.number }} --skip-from=\"dependabot[bot]\"" -------------------------------------------------------------------------------- /.github/workflows/release-description-update.yml: -------------------------------------------------------------------------------- 1 | name: "Release - Description Update" 2 | 3 | ######################################################### 4 | # WARNING - THIS ACTION WILL UPDATE RELEASE DESCRIPTION # 5 | ######################################################### 6 | 7 | on: 8 | release: 9 | types: 10 | - created 11 | 12 | jobs: 13 | release-description-update: 14 | name: "Release - Description Update" 15 | 16 | runs-on: "ubuntu-latest" 17 | 18 | steps: 19 | - name: "Update CHANGELOG" 20 | uses: "docker://aeonphp/automation:latest" 21 | env: 22 | AEON_AUTOMATION_GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 23 | with: 24 | entrypoint: "/composer/vendor/bin/automation" 25 | args: "changelog:generate ${{ github.repository }} --tag=${{ github.event.release.tag_name }} --github-release-update --skip-from=\"dependabot[bot]\" --skip-from=\"aeon-automation\"" 26 | -------------------------------------------------------------------------------- /.github/workflows/static-analyze.yml: -------------------------------------------------------------------------------- 1 | name: "Static Analyze" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - "1.x" 8 | schedule: 9 | - cron: '* 8 * * *' 10 | 11 | jobs: 12 | static-analyze: 13 | name: "Static Analyze" 14 | 15 | runs-on: ${{ matrix.operating-system }} 16 | 17 | strategy: 18 | matrix: 19 | dependencies: 20 | - "locked" 21 | php-version: 22 | - "8.1" 23 | operating-system: 24 | - "ubuntu-latest" 25 | 26 | steps: 27 | - name: "Checkout" 28 | uses: "actions/checkout@v2" 29 | 30 | - name: "Install PHP" 31 | uses: "shivammathur/setup-php@v2" 32 | with: 33 | coverage: pcov 34 | tools: composer:v2 35 | php-version: "${{ matrix.php-version }}" 36 | ini-values: memory_limit=-1 37 | 38 | - name: "Get Composer Cache Directory" 39 | id: composer-cache 40 | run: | 41 | echo "::set-output name=dir::$(composer config cache-files-dir)" 42 | 43 | - name: "Cache Composer dependencies" 44 | uses: "actions/cache@v2" 45 | with: 46 | path: | 47 | ${{ steps.composer-cache.outputs.dir }} 48 | key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-${{ hashFiles('**/composer.lock') }}" 49 | restore-keys: | 50 | php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer- 51 | 52 | - name: "Install locked dependencies" 53 | run: "composer install --no-interaction --no-progress --no-suggest" 54 | 55 | - name: "Static Analyze" 56 | run: "composer static:analyze" 57 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: "Tests" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - "1.x" 8 | schedule: 9 | - cron: '* 8 * * *' 10 | 11 | jobs: 12 | tests: 13 | name: "Tests" 14 | 15 | runs-on: ${{ matrix.operating-system }} 16 | 17 | strategy: 18 | matrix: 19 | dependencies: 20 | - "locked" 21 | - "lowest" 22 | - "highest" 23 | php-version: 24 | - "8.1" 25 | - "8.2" 26 | operating-system: 27 | - "ubuntu-latest" 28 | 29 | steps: 30 | - name: "Checkout" 31 | uses: "actions/checkout@v2" 32 | 33 | - name: "Install PHP" 34 | uses: "shivammathur/setup-php@v2" 35 | with: 36 | coverage: pcov 37 | tools: composer:v2 38 | php-version: "${{ matrix.php-version }}" 39 | ini-values: memory_limit=-1 40 | 41 | - name: "Get Composer Cache Directory" 42 | id: composer-cache 43 | run: | 44 | echo "::set-output name=dir::$(composer config cache-files-dir)" 45 | 46 | - name: "Cache Composer dependencies" 47 | uses: "actions/cache@v2" 48 | with: 49 | path: | 50 | ${{ steps.composer-cache.outputs.dir }} 51 | key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-${{ hashFiles('**/composer.lock') }}" 52 | restore-keys: | 53 | php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer- 54 | 55 | - name: "Install lowest dependencies" 56 | if: ${{ matrix.dependencies == 'lowest' }} 57 | run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest" 58 | 59 | - name: "Install highest dependencies" 60 | if: ${{ matrix.dependencies == 'highest' }} 61 | run: "composer update --no-interaction --no-progress --no-suggest" 62 | 63 | - name: "Install locked dependencies" 64 | if: ${{ matrix.dependencies == 'locked' }} 65 | run: "composer install --no-interaction --no-progress --no-suggest" 66 | 67 | - name: "Test" 68 | run: "composer test" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | *.cache 3 | var 4 | /Tests/Functional/config/app_insights_php.php -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | files() 5 | ->in([ 6 | __DIR__ . '/', 7 | ]); 8 | 9 | if (!\file_exists(__DIR__ . '/var')) { 10 | \mkdir(__DIR__ . '/var'); 11 | } 12 | 13 | /** 14 | * This configuration was taken from https://github.com/sebastianbergmann/phpunit/blob/master/.php_cs.dist 15 | * and slightly adjusted. 16 | */ 17 | $config = new PhpCsFixer\Config(); 18 | 19 | return $config 20 | ->setRiskyAllowed(true) 21 | ->setCacheFile(__DIR__.'/var/.php_cs.cache') 22 | ->setRules([ 23 | 'align_multiline_comment' => true, 24 | 'array_indentation' => true, 25 | 'array_syntax' => ['syntax' => 'short'], 26 | 'blank_line_after_namespace' => true, 27 | 'blank_line_before_statement' => [ 28 | 'statements' => [ 29 | 'break', 30 | 'continue', 31 | 'declare', 32 | 'default', 33 | 'do', 34 | 'exit', 35 | 'for', 36 | 'foreach', 37 | 'goto', 38 | 'if', 39 | 'include', 40 | 'include_once', 41 | 'require', 42 | 'require_once', 43 | 'return', 44 | 'switch', 45 | 'throw', 46 | 'try', 47 | 'while', 48 | ], 49 | ], 50 | 'braces' => true, 51 | 'cast_spaces' => true, 52 | 'class_attributes_separation' => ['elements' => ['const'=>'one', 'method'=>'one', 'property'=>'one']], 53 | 'combine_consecutive_issets' => true, 54 | 'combine_consecutive_unsets' => true, 55 | 'compact_nullable_typehint' => true, 56 | 'concat_space' => ['spacing' => 'one'], 57 | 'constant_case' => true, 58 | 'declare_equal_normalize' => ['space' => 'none'], 59 | 'declare_strict_types' => true, 60 | 'dir_constant' => true, 61 | 'elseif' => true, 62 | 'encoding' => true, 63 | 'echo_tag_syntax' => true, 64 | 'explicit_indirect_variable' => true, 65 | 'explicit_string_variable' => true, 66 | 'full_opening_tag' => true, 67 | 'fully_qualified_strict_types' => true, 68 | 'function_typehint_space' => true, 69 | 'function_declaration' => true, 70 | 'global_namespace_import' => [ 71 | 'import_classes' => false, 72 | 'import_constants' => false, 73 | 'import_functions' => false, 74 | ], 75 | 'heredoc_to_nowdoc' => true, 76 | 'increment_style' => [ 77 | 'style' => PhpCsFixer\Fixer\Operator\IncrementStyleFixer::STYLE_POST, 78 | ], 79 | 'indentation_type' => true, 80 | 'is_null' => true, 81 | 'line_ending' => true, 82 | 'list_syntax' => ['syntax' => 'short'], 83 | 'logical_operators' => true, 84 | 'lowercase_keywords' => true, 85 | 'lowercase_static_reference' => true, 86 | 'magic_constant_casing' => true, 87 | 'magic_method_casing' => true, 88 | 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'], 89 | 'modernize_types_casting' => false, 90 | 'multiline_comment_opening_closing' => true, 91 | 'multiline_whitespace_before_semicolons' => true, 92 | 'native_constant_invocation' => false, 93 | 'native_function_casing' => false, 94 | 'native_function_invocation' => ['include'=>['@all']], 95 | 'native_function_type_declaration_casing' => true, 96 | 'new_with_braces' => false, 97 | 'no_alias_functions' => true, 98 | 'no_alternative_syntax' => true, 99 | 'no_blank_lines_after_class_opening' => true, 100 | 'no_blank_lines_after_phpdoc' => true, 101 | 'no_blank_lines_before_namespace' => false, 102 | 'no_closing_tag' => true, 103 | 'no_empty_comment' => true, 104 | 'no_empty_phpdoc' => true, 105 | 'no_empty_statement' => true, 106 | 'no_extra_blank_lines' => true, 107 | 'no_homoglyph_names' => true, 108 | 'no_leading_import_slash' => true, 109 | 'no_leading_namespace_whitespace' => true, 110 | 'no_mixed_echo_print' => ['use' => 'print'], 111 | 'no_multiline_whitespace_around_double_arrow' => true, 112 | 'no_null_property_initialization' => true, 113 | 'no_php4_constructor' => true, 114 | 'no_short_bool_cast' => true, 115 | 'no_singleline_whitespace_before_semicolons' => true, 116 | 'no_spaces_after_function_name' => true, 117 | 'no_spaces_around_offset' => true, 118 | 'no_spaces_inside_parenthesis' => true, 119 | 'no_superfluous_elseif' => true, 120 | 'no_superfluous_phpdoc_tags' => false, 121 | 'no_trailing_comma_in_list_call' => true, 122 | 'no_trailing_comma_in_singleline_array' => true, 123 | 'no_trailing_whitespace' => true, 124 | 'no_trailing_whitespace_in_comment' => true, 125 | 'no_unneeded_control_parentheses' => true, 126 | 'no_unneeded_curly_braces' => true, 127 | 'no_unneeded_final_method' => true, 128 | 'no_unreachable_default_argument_value' => true, 129 | 'no_unset_on_property' => true, 130 | 'no_unused_imports' => true, 131 | 'no_useless_else' => true, 132 | 'no_useless_return' => true, 133 | 'no_whitespace_before_comma_in_array' => true, 134 | 'no_whitespace_in_blank_line' => true, 135 | 'non_printable_character' => true, 136 | 'normalize_index_brace' => true, 137 | 'object_operator_without_whitespace' => true, 138 | 'ordered_class_elements' => [ 139 | 'order' => [ 140 | 'use_trait', 141 | 'constant_public', 142 | 'constant_protected', 143 | 'constant_private', 144 | 'property_public_static', 145 | 'property_protected_static', 146 | 'property_private_static', 147 | 'property_public', 148 | 'property_protected', 149 | 'property_private', 150 | 'construct', 151 | 'method_public_static', 152 | 'destruct', 153 | 'magic', 154 | 'phpunit', 155 | 'method_public', 156 | 'method_protected', 157 | 'method_private', 158 | 'method_protected_static', 159 | 'method_private_static', 160 | ], 161 | ], 162 | 'ordered_imports' => [ 163 | 'imports_order' => [ 164 | PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_CONST, 165 | PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_FUNCTION, 166 | PhpCsFixer\Fixer\Import\OrderedImportsFixer::IMPORT_TYPE_CLASS, 167 | ] 168 | ], 169 | 'ordered_interfaces' => [ 170 | 'direction' => 'ascend', 171 | 'order' => 'alpha', 172 | ], 173 | 'phpdoc_add_missing_param_annotation' => false, 174 | 'phpdoc_align' => ['align' => 'left'], 175 | 'phpdoc_annotation_without_dot' => true, 176 | 'phpdoc_indent' => true, 177 | 'phpdoc_no_access' => true, 178 | 'phpdoc_no_empty_return' => true, 179 | 'phpdoc_no_package' => true, 180 | 'phpdoc_order' => true, 181 | 'phpdoc_return_self_reference' => true, 182 | 'phpdoc_scalar' => true, 183 | 'phpdoc_separation' => true, 184 | 'phpdoc_single_line_var_spacing' => true, 185 | 'phpdoc_summary' => true, 186 | 'phpdoc_to_comment' => false, 187 | 'phpdoc_trim' => true, 188 | 'phpdoc_trim_consecutive_blank_line_separation' => true, 189 | 'phpdoc_types' => ['groups' => ['simple', 'meta']], 190 | 'phpdoc_types_order' => true, 191 | 'phpdoc_var_without_name' => true, 192 | 'pow_to_exponentiation' => true, 193 | 'protected_to_private' => true, 194 | 'return_assignment' => true, 195 | 'return_type_declaration' => ['space_before' => 'one'], 196 | 'self_accessor' => true, 197 | 'self_static_accessor' => true, 198 | 'semicolon_after_instruction' => true, 199 | 'set_type_to_cast' => true, 200 | 'short_scalar_cast' => true, 201 | 'simple_to_complex_string_variable' => true, 202 | 'simplified_null_return' => false, 203 | 'single_blank_line_at_eof' => true, 204 | 'single_import_per_statement' => true, 205 | 'single_line_after_imports' => true, 206 | 'single_quote' => true, 207 | 'standardize_not_equals' => true, 208 | 'strict_param' => true, 209 | 'ternary_to_null_coalescing' => true, 210 | 'trailing_comma_in_multiline' => true, 211 | 'trim_array_spaces' => true, 212 | 'unary_operator_spaces' => true, 213 | 'visibility_required' => [ 214 | 'elements' => [ 215 | 'const', 216 | 'method', 217 | 'property', 218 | ], 219 | ], 220 | 'void_return' => true, 221 | 'whitespace_after_comma_in_array' => true, 222 | ]) 223 | ->setFinder($finder); -------------------------------------------------------------------------------- /AppInsightsPHPBundle.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle; 15 | 16 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\DependencyInjection\Compiler\DoctrineDependencyPassPass; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\HttpKernel\Bundle\Bundle; 19 | 20 | final class AppInsightsPHPBundle extends Bundle 21 | { 22 | public function build(ContainerBuilder $container) : void 23 | { 24 | $container->addCompilerPass(new DoctrineDependencyPassPass()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.3.1] - 2023-03-28 2 | 3 | ### Updated 4 | - [6d2e19](https://github.com/app-insights-php/app-insights-php-bundle/commit/6d2e19d2a70fcc262c990cdb361aecc2af7ba2ec) - **symfony dependencies** - [@norberttech](https://github.com/norberttech) 5 | 6 | ## [0.3.0] - 2023-03-28 7 | 8 | ### Updated 9 | - [bfea42](https://github.com/app-insights-php/app-insights-php-bundle/commit/bfea420bad6faa4ddda45fe0213471392798bc54) - **php version to php ~8.1||~8.2** - [@norberttech](https://github.com/norberttech) 10 | 11 | ## [0.2.9] - 2022-08-09 12 | 13 | ### Updated 14 | - [231d81](https://github.com/app-insights-php/app-insights-php-bundle/commit/231d810e8efc5ec05ffd2c4512dd9d20c5231564) - **dependencieS** - [@norberttech](https://github.com/norberttech) 15 | 16 | ## [0.2.8] - 2022-08-02 17 | 18 | ### Updated 19 | - [8e8d14](https://github.com/app-insights-php/app-insights-php-bundle/commit/8e8d14417d488f808aefd575ee2b34b5690ab8ea) - **client to the latest version** - [@norberttech](https://github.com/norberttech) 20 | - [1be250](https://github.com/app-insights-php/app-insights-php-bundle/commit/1be250cd511871379bb71763e9be6abca76088f8) - **dependencies** - [@norberttech](https://github.com/norberttech) 21 | 22 | ### Removed 23 | - [bd2655](https://github.com/app-insights-php/app-insights-php-bundle/commit/bd2655c9852a5d4ce599e4cb21171f06c3696dcf) - **var from git repo** - [@norberttech](https://github.com/norberttech) 24 | 25 | ## [0.2.7] - 2022-04-15 26 | 27 | ### Changed 28 | - [#112](https://github.com/app-insights-php/app-insights-php-bundle/pull/112) - **Updated project to php 8.1** - [@norberttech](https://github.com/norberttech) 29 | 30 | ## [0.2.6] - 2021-02-14 31 | 32 | ### Added 33 | - [#39](https://github.com/app-insights-php/app-insights-php-bundle/pull/39) - **Support for Twig ^3.0** - [@norberttech](https://github.com/norberttech) 34 | 35 | ### Changed 36 | - [#39](https://github.com/app-insights-php/app-insights-php-bundle/pull/39) - **Moved from XML to PHP bundle configuration** - [@norberttech](https://github.com/norberttech) 37 | - [de4c55](https://github.com/app-insights-php/app-insights-php-bundle/commit/de4c557604dbb52764cc4b8677b1efc5ece1e15c) - **dependencies** - [@norberttech](https://github.com/norberttech) 38 | - [779365](https://github.com/app-insights-php/app-insights-php-bundle/commit/779365cb122cbb45071d759c01b48bcc80e53a53) - **PHP 8 update, aeon-php/automation integration, updated dependencies** - [@norberttech](https://github.com/norberttech) 39 | 40 | ## [0.2.5] - 2020-06-25 41 | 42 | ### Changed 43 | - [#35](https://github.com/app-insights-php/app-insights-php-bundle/pull/35) - **Do not execute listeners when telemetry is disabled** - [@norberttech](https://github.com/norberttech) 44 | 45 | ## [0.2.4] - 2020-01-21 46 | 47 | ### Fixed 48 | - [#28](https://github.com/app-insights-php/app-insights-php-bundle/pull/28) - **replace classes that have been removed from Symfony 5** - [@smindel](https://github.com/smindel) 49 | 50 | ## [0.2.3] - 2020-01-16 51 | 52 | ### Changed 53 | - [#26](https://github.com/app-insights-php/app-insights-php-bundle/pull/26) - **Reverted change in folders structure, moved to github actions, added …** - [@norberttech](https://github.com/norberttech) 54 | 55 | ### Fixed 56 | - [#27](https://github.com/app-insights-php/app-insights-php-bundle/pull/27) - **cache action** - [@norberttech](https://github.com/norberttech) 57 | 58 | ## [0.2.2] - 2019-09-04 59 | 60 | ### Changed 61 | - [#22](https://github.com/app-insights-php/app-insights-php-bundle/pull/22) - **Update app-insights-client** - [@tomaszhanc](https://github.com/tomaszhanc) 62 | - [cbf410](https://github.com/app-insights-php/app-insights-php-bundle/commit/cbf410654881a013a1c3d006fb7fc2cc9fea1672) - **Update README.md** - [@norberttech](https://github.com/norberttech) 63 | 64 | ## [0.2.1] - 2019-07-25 65 | 66 | ### Changed 67 | - [#19](https://github.com/app-insights-php/app-insights-php-bundle/pull/19) - **Log more parameters for a request** - [@tomaszhanc](https://github.com/tomaszhanc) 68 | 69 | ## [0.2.0] - 2019-07-04 70 | 71 | ### Changed 72 | - [#18](https://github.com/app-insights-php/app-insights-php-bundle/pull/18) - **Remove failure cache from KernelTerminateListener** - [@tomaszhanc](https://github.com/tomaszhanc) 73 | 74 | ## [0.1.15] - 2019-06-28 75 | 76 | ### Changed 77 | - [#17](https://github.com/app-insights-php/app-insights-php-bundle/pull/17) - **Move checking instrumentation key directly into listeners** - [@norberttech](https://github.com/norberttech) 78 | 79 | ## [0.1.14] - 2019-06-28 80 | 81 | ### Changed 82 | - [#16](https://github.com/app-insights-php/app-insights-php-bundle/pull/16) - **Improve registration of symfony listeners** - [@norberttech](https://github.com/norberttech) 83 | 84 | ## [0.1.13] - 2019-06-27 85 | 86 | ### Added 87 | - [#15](https://github.com/app-insights-php/app-insights-php-bundle/pull/15) - **possibility to cache failed app insights flushes** - [@norberttech](https://github.com/norberttech) 88 | 89 | ### Changed 90 | - [339d08](https://github.com/app-insights-php/app-insights-php-bundle/commit/339d089a95b4eb2d1a5b7ca11f5378b1a0172287) - **Update index.md** - [@norberttech](https://github.com/norberttech) 91 | 92 | ## [0.1.12] - 2019-06-26 93 | 94 | ### Added 95 | - [#14](https://github.com/app-insights-php/app-insights-php-bundle/pull/14) - **telemetry client channel content to fallback logger** - [@norberttech](https://github.com/norberttech) 96 | 97 | ## [0.1.11] - 2019-06-21 98 | 99 | ### Added 100 | - [#13](https://github.com/app-insights-php/app-insights-php-bundle/pull/13) - **fallback logger to KernelTerminateListener** - [@norberttech](https://github.com/norberttech) 101 | 102 | ## [0.1.10] - 2019-04-27 103 | 104 | ### Changed 105 | - [#10](https://github.com/app-insights-php/app-insights-php-bundle/pull/10) - **Feature/track exception cli** - [@norberttech](https://github.com/norberttech) 106 | 107 | ## [0.1.9] - 2019-04-18 108 | 109 | ### Added 110 | - [#9](https://github.com/app-insights-php/app-insights-php-bundle/pull/9) - **CLI Telemetry client commands** - [@norberttech](https://github.com/norberttech) 111 | 112 | ## [0.1.8] - 2019-04-15 113 | 114 | ### Changed 115 | - [#8](https://github.com/app-insights-php/app-insights-php-bundle/pull/8) - **Do not track users through JS client when app insights configuration …** - [@norberttech](https://github.com/norberttech) 116 | 117 | ## [0.1.7] - 2019-04-06 118 | 119 | ### Changed 120 | - [#7](https://github.com/app-insights-php/app-insights-php-bundle/pull/7) - **Don't mark 3XX status code as not successful** - [@javiermadueno](https://github.com/javiermadueno) 121 | - [a9bc7d](https://github.com/app-insights-php/app-insights-php-bundle/commit/a9bc7dc91c45c829e7877bf4ef2421fa0f68205d) - **Update README.md** - [@norberttech](https://github.com/norberttech) 122 | 123 | ## [0.1.6] - 2019-03-03 124 | 125 | ### Added 126 | - [#5](https://github.com/app-insights-php/app-insights-php-bundle/pull/5) - **support for app insights monolog dependency handler** - [@norberttech](https://github.com/norberttech) 127 | 128 | ## [0.1.5] - 2019-03-03 129 | 130 | ### Added 131 | - [7c3f68](https://github.com/app-insights-php/app-insights-php-bundle/commit/7c3f68040df8407639319a57f4c701915a915a3e) - **adding file headers to cs fixer rule** - [@norberttech](https://github.com/norberttech) 132 | 133 | ### Fixed 134 | - [a5663e](https://github.com/app-insights-php/app-insights-php-bundle/commit/a5663e82aeaf5f5ae6c9e07d93063b03182c6831) - **failing tests** - [@norberttech](https://github.com/norberttech) 135 | - [aedb90](https://github.com/app-insights-php/app-insights-php-bundle/commit/aedb9008eca154dfb38f02ee0f1eb9572cd4dcc1) - **setting operation id in page view** - [@norberttech](https://github.com/norberttech) 136 | 137 | ## [0.1.4] - 2019-03-03 138 | 139 | ### Added 140 | - [c0df76](https://github.com/app-insights-php/app-insights-php-bundle/commit/c0df7623459828b30d57666b7a8ee69b53c86355) - **missing is_safe option to TelemetryTwig Extension** - [@norberttech](https://github.com/norberttech) 141 | 142 | ## [0.1.3] - 2019-03-03 143 | 144 | ### Added 145 | - [#3](https://github.com/app-insights-php/app-insights-php-bundle/pull/3) - **query parameters as a properties to request logger** - [@norberttech](https://github.com/norberttech) 146 | 147 | ### Changed 148 | - [#4](https://github.com/app-insights-php/app-insights-php-bundle/pull/4) - **Twig helper for PageView tracking integration & docs update** - [@norberttech](https://github.com/norberttech) 149 | 150 | ## [0.1.2] - 2019-02-27 151 | 152 | ### Changed 153 | - [#2](https://github.com/app-insights-php/app-insights-php-bundle/pull/2) - **dependencies** - [@norberttech](https://github.com/norberttech) 154 | 155 | ## [0.1.1] - 2019-02-22 156 | 157 | ### Added 158 | - [#1](https://github.com/app-insights-php/app-insights-php-bundle/pull/1) - **possibility to register many monolog handlers** - [@norberttech](https://github.com/norberttech) 159 | - [91a81b](https://github.com/app-insights-php/app-insights-php-bundle/commit/91a81b971c59b9d70eccdbd66e076643f2913508) - **master branch alias in composer.json** - [@norberttech](https://github.com/norberttech) 160 | 161 | ## [0.1.0] - 2019-02-21 162 | 163 | ### Changed 164 | - [79ae97](https://github.com/app-insights-php/app-insights-php-bundle/commit/79ae975fb9ecc36b3fe3118be7e04604f56e13cf) - **readme** - [@norberttech](https://github.com/norberttech) 165 | - [2d15f0](https://github.com/app-insights-php/app-insights-php-bundle/commit/2d15f05d13fcad55f21dc1adcd7e505671c91c1d) - **Simplified travis testsuite** - [@norberttech](https://github.com/norberttech) 166 | - [8018f1](https://github.com/app-insights-php/app-insights-php-bundle/commit/8018f1498180ab21765cf3357608dec852827f34) - **Update README.md** - [@norberttech](https://github.com/norberttech) 167 | - [41426a](https://github.com/app-insights-php/app-insights-php-bundle/commit/41426a847bc628eadd27ad25f07fff7f27965ead) - **Update README.md** - [@norberttech](https://github.com/norberttech) 168 | - [6a4c59](https://github.com/app-insights-php/app-insights-php-bundle/commit/6a4c591c20e462283adb48f9e688eb6516efec11) - **dependencies to stable versions** - [@norberttech](https://github.com/norberttech) 169 | - [6874e5](https://github.com/app-insights-php/app-insights-php-bundle/commit/6874e5a4597077c44319ec961cafe365e5b96ef2) - **cs-fixer dependency** - [@norberttech](https://github.com/norberttech) 170 | - [965ff9](https://github.com/app-insights-php/app-insights-php-bundle/commit/965ff9cf4678312b9c73044e5701e61ace6a1f27) - **Initial commit** - [@norberttech](https://github.com/norberttech) 171 | - [15f8f9](https://github.com/app-insights-php/app-insights-php-bundle/commit/15f8f938647381efae8c7d6e77afa214819754e0) - **Initial commit** - [@norberttech](https://github.com/norberttech) 172 | 173 | ### Fixed 174 | - [1f4466](https://github.com/app-insights-php/app-insights-php-bundle/commit/1f44661f3d236c1d68917ded0a1c177da145cc56) - **lowest-dependencies test by forcing guzzle version 6 and higher** - [@norberttech](https://github.com/norberttech) 175 | - [3de06b](https://github.com/app-insights-php/app-insights-php-bundle/commit/3de06bae02f0317915d35f9c5f6574adf1f85f19) - **composer php cs script** - [@norberttech](https://github.com/norberttech) 176 | - [b07a04](https://github.com/app-insights-php/app-insights-php-bundle/commit/b07a0443233983515ad9427dd9ea55a545f890d9) - **tests** - [@norberttech](https://github.com/norberttech) 177 | - [a56f58](https://github.com/app-insights-php/app-insights-php-bundle/commit/a56f58b8779452e999dcca586065cdc31ba2aaad) - **invalid dependencies** - [@norberttech](https://github.com/norberttech) 178 | 179 | Generated by [Automation](https://github.com/aeon-php/automation) -------------------------------------------------------------------------------- /Cache/NullCache.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Cache; 15 | 16 | use Psr\SimpleCache\CacheInterface; 17 | 18 | final class NullCache implements CacheInterface 19 | { 20 | public function get($key, $default = null) : void 21 | { 22 | } 23 | 24 | public function set($key, $value, $ttl = null) : void 25 | { 26 | } 27 | 28 | public function delete($key) : void 29 | { 30 | } 31 | 32 | public function clear() : void 33 | { 34 | } 35 | 36 | public function getMultiple($keys, $default = null) : void 37 | { 38 | } 39 | 40 | public function setMultiple($values, $ttl = null) : void 41 | { 42 | } 43 | 44 | public function deleteMultiple($keys) : void 45 | { 46 | } 47 | 48 | public function has($key) 49 | { 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Command/TrackDependencyCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use Symfony\Component\Console\Command\Command; 18 | use Symfony\Component\Console\Input\InputArgument; 19 | use Symfony\Component\Console\Input\InputInterface; 20 | use Symfony\Component\Console\Input\InputOption; 21 | use Symfony\Component\Console\Output\OutputInterface; 22 | use Symfony\Component\Console\Style\SymfonyStyle; 23 | 24 | final class TrackDependencyCommand extends Command 25 | { 26 | public const NAME = 'app-insights:track:dependency'; 27 | 28 | protected static $defaultName = self::NAME; 29 | 30 | private $client; 31 | 32 | public function __construct(Client $client) 33 | { 34 | parent::__construct(); 35 | $this->client = $client; 36 | } 37 | 38 | protected function configure() : void 39 | { 40 | $this 41 | ->setDescription('[App Insights] Track Dependency.') 42 | ->addArgument('name', InputArgument::REQUIRED, 'Dependency name') 43 | ->addOption('type', null, InputOption::VALUE_OPTIONAL, 'Dependency type', '') 44 | ->addOption('commandName', null, InputOption::VALUE_OPTIONAL, 'Dependency command name', '') 45 | ->addOption('startTime', null, InputOption::VALUE_OPTIONAL, 'Start time (timestamp) when call to dependency was initialized', \time()) 46 | ->addOption('durationTime', null, InputOption::VALUE_OPTIONAL, 'Dependency call duration time in milliseconds', 0) 47 | ->addOption('isSuccessful', null, InputOption::VALUE_OPTIONAL, 'Was the dependency call successful', true) 48 | ->addOption('resultCode', null, InputOption::VALUE_OPTIONAL, 'Dependency result code') 49 | ->addOption('properties', null, InputOption::VALUE_OPTIONAL, 'Dependency additional properties passed as json object') 50 | ->addOption('dont-flush', null, InputOption::VALUE_OPTIONAL, 'Don\'t flush client directly in the command, wait for the KernelTerminateListener', false); 51 | } 52 | 53 | protected function execute(InputInterface $input, OutputInterface $output) : int 54 | { 55 | $io = new SymfonyStyle($input, $output); 56 | 57 | $this->client->trackDependency( 58 | $input->getArgument('name'), 59 | $input->getOption('type'), 60 | $input->getOption('commandName'), 61 | (int) $input->getOption('startTime'), 62 | (int) $input->getOption('durationTime'), 63 | (bool) $input->getOption('isSuccessful'), 64 | $input->getOption('resultCode'), 65 | $input->getOption('properties') ? \json_decode($input->getOption('properties'), true) : null 66 | ); 67 | 68 | $dontFlush = false !== $input->getOption('dont-flush'); 69 | 70 | if ($dontFlush) { 71 | $io->success('Telemetry sent.'); 72 | 73 | return 0; 74 | } 75 | 76 | $response = $this->client->flush(); 77 | 78 | if (null === $response) { 79 | $io->warning('Telemetry was not sent.'); 80 | $io->note('Configuration is disabled or there was an error. Please check fallback logs.'); 81 | 82 | return 0; 83 | } 84 | 85 | if (200 === $response->getStatusCode()) { 86 | $io->success('Telemetry successfully sent.'); 87 | $io->note((string) $response->getBody()); 88 | } else { 89 | $io->success('Something went wrong.'); 90 | $io->note('Status Code: ' . $response->getStatusCode()); 91 | $io->note((string) $response->getBody()); 92 | } 93 | 94 | return 0; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Command/TrackEventCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use Symfony\Component\Console\Command\Command; 18 | use Symfony\Component\Console\Input\InputArgument; 19 | use Symfony\Component\Console\Input\InputInterface; 20 | use Symfony\Component\Console\Input\InputOption; 21 | use Symfony\Component\Console\Output\OutputInterface; 22 | use Symfony\Component\Console\Style\SymfonyStyle; 23 | 24 | final class TrackEventCommand extends Command 25 | { 26 | public const NAME = 'app-insights:track:event'; 27 | 28 | protected static $defaultName = self::NAME; 29 | 30 | private $client; 31 | 32 | public function __construct(Client $client) 33 | { 34 | parent::__construct(); 35 | $this->client = $client; 36 | } 37 | 38 | protected function configure() : void 39 | { 40 | $this 41 | ->setDescription('[App Insights] Track Event.') 42 | ->addArgument('name', InputArgument::REQUIRED, 'Event name') 43 | ->addOption('properties', null, InputOption::VALUE_OPTIONAL, 'Event additional properties passed as json object') 44 | ->addOption('measurements', null, InputOption::VALUE_OPTIONAL, 'Event additional measurements passed as json object') 45 | ->addOption('dont-flush', null, InputOption::VALUE_OPTIONAL, 'Don\'t flush client directly in the command, wait for the KernelTerminateListener', false); 46 | } 47 | 48 | protected function execute(InputInterface $input, OutputInterface $output) : int 49 | { 50 | $io = new SymfonyStyle($input, $output); 51 | 52 | $this->client->trackEvent( 53 | $input->getArgument('name'), 54 | $input->getOption('properties') ? \json_decode($input->getOption('properties'), true) : null, 55 | $input->getOption('measurements') ? \json_decode($input->getOption('measurements'), true) : null 56 | ); 57 | 58 | $dontFlush = false !== $input->getOption('dont-flush'); 59 | 60 | if ($dontFlush) { 61 | $io->success('Telemetry sent.'); 62 | 63 | return 0; 64 | } 65 | 66 | $response = $this->client->flush(); 67 | 68 | if (null === $response) { 69 | $io->warning('Telemetry was not sent.'); 70 | $io->note('Configuration is disabled or there was an error. Please check fallback logs.'); 71 | 72 | return 0; 73 | } 74 | 75 | if (200 === $response->getStatusCode()) { 76 | $io->success('Telemetry successfully sent.'); 77 | $io->note((string) $response->getBody()); 78 | } else { 79 | $io->success('Something went wrong.'); 80 | $io->note('Status Code: ' . $response->getStatusCode()); 81 | $io->note((string) $response->getBody()); 82 | } 83 | 84 | return 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Command/TrackExceptionCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use Symfony\Component\Console\Command\Command; 18 | use Symfony\Component\Console\Input\InputArgument; 19 | use Symfony\Component\Console\Input\InputInterface; 20 | use Symfony\Component\Console\Input\InputOption; 21 | use Symfony\Component\Console\Output\OutputInterface; 22 | use Symfony\Component\Console\Style\SymfonyStyle; 23 | 24 | final class TrackExceptionCommand extends Command 25 | { 26 | public const NAME = 'app-insights:track:exception'; 27 | 28 | protected static $defaultName = self::NAME; 29 | 30 | private $client; 31 | 32 | public function __construct(Client $client) 33 | { 34 | parent::__construct(); 35 | $this->client = $client; 36 | } 37 | 38 | protected function configure() : void 39 | { 40 | $this 41 | ->setDescription('[App Insights] Track Exception.') 42 | ->addArgument('class', InputArgument::OPTIONAL, 'Exception class', '\\Exception') 43 | ->addArgument('message', InputArgument::OPTIONAL, 'Exception message', '') 44 | ->addOption('properties', null, InputOption::VALUE_OPTIONAL, 'Exception additional properties passed as json object') 45 | ->addOption('measurements', null, InputOption::VALUE_OPTIONAL, 'Exception additional measurements passed as json object') 46 | ->addOption('dont-flush', null, InputOption::VALUE_OPTIONAL, 'Don\'t flush client directly in the command, wait for the KernelTerminateListener', false); 47 | } 48 | 49 | protected function initialize(InputInterface $input, OutputInterface $output) : void 50 | { 51 | if (!\class_exists($input->getArgument('class'))) { 52 | throw new \InvalidArgumentException('Argument class must be a valid class'); 53 | } 54 | } 55 | 56 | protected function execute(InputInterface $input, OutputInterface $output) : int 57 | { 58 | $io = new SymfonyStyle($input, $output); 59 | 60 | $class = $input->getArgument('class'); 61 | 62 | $this->client->trackException( 63 | new $class($input->getArgument('message')), 64 | $input->getOption('properties') ? \json_decode($input->getOption('properties'), true) : null, 65 | $input->getOption('measurements') ? \json_decode($input->getOption('measurements'), true) : null 66 | ); 67 | 68 | $dontFlush = false !== $input->getOption('dont-flush'); 69 | 70 | if ($dontFlush) { 71 | $io->success('Telemetry sent.'); 72 | 73 | return 0; 74 | } 75 | 76 | $response = $this->client->flush(); 77 | 78 | if (null === $response) { 79 | $io->warning('Telemetry was not sent.'); 80 | $io->note('Configuration is disabled or there was an error. Please check fallback logs.'); 81 | 82 | return 0; 83 | } 84 | 85 | if (200 === $response->getStatusCode()) { 86 | $io->success('Telemetry successfully sent.'); 87 | $io->note((string) $response->getBody()); 88 | } else { 89 | $io->success('Something went wrong.'); 90 | $io->note('Status Code: ' . $response->getStatusCode()); 91 | $io->note((string) $response->getBody()); 92 | } 93 | 94 | return 0; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Command/TrackMetricCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use ApplicationInsights\Channel\Contracts\Data_Point_Type; 18 | use Symfony\Component\Console\Command\Command; 19 | use Symfony\Component\Console\Input\InputArgument; 20 | use Symfony\Component\Console\Input\InputInterface; 21 | use Symfony\Component\Console\Input\InputOption; 22 | use Symfony\Component\Console\Output\OutputInterface; 23 | use Symfony\Component\Console\Style\SymfonyStyle; 24 | 25 | final class TrackMetricCommand extends Command 26 | { 27 | public const NAME = 'app-insights:track:metric'; 28 | 29 | protected static $defaultName = self::NAME; 30 | 31 | private $client; 32 | 33 | public function __construct(Client $client) 34 | { 35 | parent::__construct(); 36 | $this->client = $client; 37 | } 38 | 39 | protected function configure() : void 40 | { 41 | $this 42 | ->setDescription('[App Insights] Track Metric.') 43 | ->addArgument('name', InputArgument::REQUIRED, 'Metric name') 44 | ->addArgument('value', InputArgument::REQUIRED, 'Metric value') 45 | ->addOption('type', null, InputOption::VALUE_OPTIONAL, 'Metric type, 0 = Measurement, 1 = Aggregation', Data_Point_Type::Measurement) 46 | ->addOption('count', null, InputOption::VALUE_OPTIONAL, 'Metric count') 47 | ->addOption('min', null, InputOption::VALUE_OPTIONAL, 'Metric max') 48 | ->addOption('max', null, InputOption::VALUE_OPTIONAL, 'Metric max') 49 | ->addOption('standardDeviation', null, InputOption::VALUE_OPTIONAL, 'Standard deviation') 50 | ->addOption('measurements', null, InputOption::VALUE_OPTIONAL, 'Metric additional measurements passed as json object') 51 | ->addOption('dont-flush', null, InputOption::VALUE_OPTIONAL, 'Don\'t flush client directly in the command, wait for the KernelTerminateListener', false); 52 | } 53 | 54 | protected function initialize(InputInterface $input, OutputInterface $output) : void 55 | { 56 | if (!\is_numeric($input->getArgument('value'))) { 57 | throw new \InvalidArgumentException('Argument value must be a valid number'); 58 | } 59 | 60 | if ($input->getOption('type') && !\in_array($input->getOption('type'), [Data_Point_Type::Measurement, Data_Point_Type::Aggregation], true)) { 61 | throw new \InvalidArgumentException('Invalid measurement type'); 62 | } 63 | } 64 | 65 | protected function execute(InputInterface $input, OutputInterface $output) : int 66 | { 67 | $io = new SymfonyStyle($input, $output); 68 | 69 | $this->client->trackMetric( 70 | $input->getArgument('name'), 71 | (float) $input->getArgument('value'), 72 | $input->getOption('type'), 73 | $input->getOption('count') ? (int) $input->getOption('count') : null, 74 | $input->getOption('min') ? (int) $input->getOption('min') : null, 75 | $input->getOption('max') ? (int) $input->getOption('max') : null, 76 | $input->getOption('standardDeviation') ? (float) $input->getOption('standardDeviation') : null, 77 | $input->getOption('measurements') ? \json_decode($input->getOption('measurements'), true) : null 78 | ); 79 | 80 | $dontFlush = false !== $input->getOption('dont-flush'); 81 | 82 | if ($dontFlush) { 83 | $io->success('Telemetry sent.'); 84 | 85 | return 0; 86 | } 87 | 88 | $response = $this->client->flush(); 89 | 90 | if (null === $response) { 91 | $io->warning('Telemetry was not sent.'); 92 | $io->note('Configuration is disabled or there was an error. Please check fallback logs.'); 93 | 94 | return 0; 95 | } 96 | 97 | if (200 === $response->getStatusCode()) { 98 | $io->success('Telemetry successfully sent.'); 99 | $io->note((string) $response->getBody()); 100 | } else { 101 | $io->success('Something went wrong.'); 102 | $io->note('Status Code: ' . $response->getStatusCode()); 103 | $io->note((string) $response->getBody()); 104 | } 105 | 106 | return 0; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /DependencyInjection/AppInsightsPHPExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\DependencyInjection; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Client\Configuration; 18 | use AppInsightsPHP\Client\Configuration\Dependenies; 19 | use AppInsightsPHP\Client\Configuration\Exceptions; 20 | use AppInsightsPHP\Client\Configuration\Requests; 21 | use AppInsightsPHP\Client\Configuration\Traces; 22 | use AppInsightsPHP\Monolog\Handler\AppInsightsDependencyHandler; 23 | use AppInsightsPHP\Monolog\Handler\AppInsightsTraceHandler; 24 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Cache\NullCache; 25 | use Psr\Log\NullLogger; 26 | use Symfony\Component\Config\FileLocator; 27 | use Symfony\Component\DependencyInjection\ContainerBuilder; 28 | use Symfony\Component\DependencyInjection\Definition; 29 | use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; 30 | use Symfony\Component\DependencyInjection\Reference; 31 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; 32 | 33 | final class AppInsightsPHPExtension extends Extension 34 | { 35 | public function load(array $configs, ContainerBuilder $container) : void 36 | { 37 | $config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs); 38 | 39 | $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); 40 | $loader->load('app_insights_php.php'); 41 | $loader->load('app_insights_php_console.php'); 42 | 43 | $container->setParameter('app_insights_php.instrumentation_key', $config['instrumentation_key']); 44 | $container->setParameter('app_insights_php.doctrine.track_dependency', $config['doctrine']['track_dependency']); 45 | 46 | // Make autowiring possible 47 | $container->setAlias(Client::class, 'app_insights_php.telemetry')->setPublic(true); 48 | 49 | $container->setDefinition( 50 | 'app_insights_php.configuration.exceptions', 51 | new Definition(Exceptions::class, [ 52 | $config['exceptions']['enabled'], 53 | (array) $config['exceptions']['ignored_exceptions'], 54 | ]) 55 | ); 56 | $container->setDefinition( 57 | 'app_insights_php.configuration.dependencies', 58 | new Definition(Dependenies::class, [ 59 | $config['dependencies']['enabled'], 60 | ]) 61 | ); 62 | $container->setDefinition( 63 | 'app_insights_php.configuration.requests', 64 | new Definition(Requests::class, [ 65 | $config['requests']['enabled'], 66 | ]) 67 | ); 68 | $container->setDefinition( 69 | 'app_insights_php.configuration.traces', 70 | new Definition(Traces::class, [ 71 | $config['traces']['enabled'], 72 | ]) 73 | ); 74 | $container->setDefinition( 75 | 'app_insights_php.configuration', 76 | new Definition(Configuration::class, [ 77 | $config['enabled'], 78 | $config['gzip_enabled'], 79 | new Reference('app_insights_php.configuration.exceptions'), 80 | new Reference('app_insights_php.configuration.dependencies'), 81 | new Reference('app_insights_php.configuration.requests'), 82 | new Reference('app_insights_php.configuration.traces'), 83 | ]) 84 | ); 85 | 86 | $container 87 | ->getDefinition('app_insights_php.telemetry.factory') 88 | ->replaceArgument(1, new Reference('app_insights_php.configuration')); 89 | 90 | if ((bool) $config['failure_cache_service_id']) { 91 | $container->getDefinition('app_insights_php.telemetry.factory') 92 | ->replaceArgument(2, new Reference($config['failure_cache_service_id'])); 93 | } else { 94 | $container->setDefinition('app_insights_php.failure_cache.null', new Definition(NullCache::class)); 95 | $container->getDefinition('app_insights_php.telemetry.factory') 96 | ->replaceArgument(2, new Reference('app_insights_php.failure_cache.null')); 97 | } 98 | 99 | if ((bool) $config['fallback_logger']) { 100 | $container->getDefinition('app_insights_php.telemetry.factory') 101 | ->replaceArgument(3, new Reference($config['fallback_logger']['service_id'])); 102 | 103 | if (isset($config['fallback_logger']['monolog_channel'])) { 104 | $container->getDefinition('app_insights_php.telemetry.factory') 105 | ->addTag('monolog.logger', ['channel' => $config['fallback_logger']['monolog_channel']]); 106 | } 107 | } else { 108 | $container->setDefinition('app_insights_php.logger.null', new Definition(NullLogger::class)); 109 | $container->getDefinition('app_insights_php.telemetry.factory') 110 | ->replaceArgument(3, new Reference('app_insights_php.logger.null')); 111 | } 112 | 113 | // Symfony 114 | if ($config['enabled']) { 115 | $loader->load('app_insights_php_symfony.php'); 116 | } 117 | 118 | // Twig 119 | if (\class_exists('Twig_Environment') || \class_exists('Twig\\Environment')) { 120 | $loader->load('app_insights_php_twig.php'); 121 | } 122 | 123 | // Doctrine 124 | if ($config['doctrine']['track_dependency']) { 125 | if (!\class_exists('AppInsightsPHP\\Doctrine\\DBAL\\Logging\\DependencyLogger')) { 126 | throw new \RuntimeException('Please first run `composer require download app-insights-php/doctrine-dependency-logger` if you want to log DBAL queries.'); 127 | } 128 | 129 | $loader->load('app_insights_php_doctrine.php'); 130 | } 131 | 132 | // Monolog 133 | if (\count($config['monolog']['handlers'])) { 134 | foreach ($config['monolog']['handlers'] as $name => $handlerConfig) { 135 | $id = \sprintf(\sprintf('app_insights_php.monolog.handler.%s', $name)); 136 | 137 | switch ($handlerConfig['type']) { 138 | case 'trace': 139 | $class = AppInsightsTraceHandler::class; 140 | $arguments = [ 141 | new Reference('app_insights_php.telemetry'), 142 | $this->levelToMonologConst($handlerConfig['level']), 143 | (bool) $handlerConfig['bubble'], 144 | ]; 145 | 146 | break; 147 | case 'dependency': 148 | $class = AppInsightsDependencyHandler::class; 149 | $arguments = [ 150 | new Reference('app_insights_php.telemetry'), 151 | ]; 152 | 153 | break; 154 | 155 | default: 156 | throw new \RuntimeException('Unrecognized monolog handler type %s', $handlerConfig['type']); 157 | } 158 | 159 | $container->register($id, $class) 160 | ->setArguments($arguments) 161 | ->setPublic(false); 162 | } 163 | } 164 | } 165 | 166 | private function levelToMonologConst($level) 167 | { 168 | return \is_int($level) ? $level : \constant('Monolog\Logger::' . \strtoupper($level)); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /DependencyInjection/Compiler/DoctrineDependencyPassPass.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | 20 | final class DoctrineDependencyPassPass implements CompilerPassInterface 21 | { 22 | public function process(ContainerBuilder $container) : void 23 | { 24 | if (false === $container->getParameter('app_insights_php.doctrine.track_dependency')) { 25 | return; 26 | } 27 | 28 | $doctrine = $container->getParameter('doctrine.connections'); 29 | 30 | foreach ($doctrine as $connectionId) { 31 | $container 32 | ->getDefinition(\sprintf('%s.configuration', $connectionId)) 33 | ->addMethodCall('setSQLLogger', [new Reference('app_insights_php.doctrine.logger.dependency')]); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\DependencyInjection; 15 | 16 | use Monolog\Logger; 17 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 18 | use Symfony\Component\Config\Definition\ConfigurationInterface; 19 | 20 | final class Configuration implements ConfigurationInterface 21 | { 22 | public function getConfigTreeBuilder() 23 | { 24 | $treeBuilder = new TreeBuilder('app_insights_php'); 25 | $rootNode = \method_exists(TreeBuilder::class, 'getRootNode') ? $treeBuilder->getRootNode() : $treeBuilder->root('app_insights_php'); 26 | 27 | $allowedLoggerTypes = ['trace', 'dependency']; 28 | 29 | $rootNode 30 | ->children() 31 | ->booleanNode('enabled')->defaultTrue()->end() 32 | ->booleanNode('gzip_enabled')->defaultFalse()->end() 33 | ->scalarNode('instrumentation_key')->isRequired()->end() 34 | ->arrayNode('fallback_logger') 35 | ->addDefaultsIfNotSet() 36 | ->children() 37 | ->scalarNode('service_id')->end() 38 | ->scalarNode('monolog_channel')->end() 39 | ->end() 40 | ->end() 41 | ->scalarNode('failure_cache_service_id')->defaultNull()->end() 42 | ->arrayNode('exceptions') 43 | ->addDefaultsIfNotSet() 44 | ->children() 45 | ->booleanNode('enabled')->defaultTrue()->end() 46 | ->arrayNode('ignored_exceptions') 47 | ->scalarPrototype()->end() 48 | ->end() 49 | ->end() 50 | ->end() 51 | ->arrayNode('dependencies') 52 | ->addDefaultsIfNotSet() 53 | ->children() 54 | ->booleanNode('enabled')->defaultTrue()->end() 55 | ->end() 56 | ->end() 57 | ->arrayNode('traces') 58 | ->addDefaultsIfNotSet() 59 | ->children() 60 | ->booleanNode('enabled')->defaultTrue()->end() 61 | ->end() 62 | ->end() 63 | ->arrayNode('requests') 64 | ->addDefaultsIfNotSet() 65 | ->children() 66 | ->booleanNode('enabled')->defaultTrue()->end() 67 | ->end() 68 | ->end() 69 | ->arrayNode('doctrine') 70 | ->addDefaultsIfNotSet() 71 | ->children() 72 | ->booleanNode('track_dependency')->defaultFalse()->end() 73 | ->end() 74 | ->end() 75 | ->arrayNode('monolog') 76 | ->addDefaultsIfNotSet() 77 | ->children() 78 | ->arrayNode('handlers') 79 | ->canBeUnset() 80 | ->useAttributeAsKey('name') 81 | ->arrayPrototype() 82 | ->children() 83 | ->scalarNode('name')->end() 84 | ->scalarNode('level')->defaultValue(Logger::DEBUG)->end() 85 | ->scalarNode('bubble')->defaultTrue()->end() 86 | ->scalarNode('type') 87 | ->defaultValue('trace') 88 | ->validate() 89 | ->ifNotInArray($allowedLoggerTypes) 90 | ->thenInvalid(\sprintf('Allowed types: [%s]', \implode(', ', $allowedLoggerTypes))) 91 | ->end() 92 | ->end() 93 | ->end() 94 | ->end() 95 | ->end() 96 | ->end() 97 | ->end(); 98 | 99 | return $treeBuilder; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /FlatArray.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle; 15 | 16 | final class FlatArray 17 | { 18 | private $array; 19 | 20 | public function __construct(array $array) 21 | { 22 | $this->array = $array; 23 | } 24 | 25 | public function __invoke() : array 26 | { 27 | return $this->flatterArray($this->array); 28 | } 29 | 30 | private function flatterArray(array $array, string $prefix = '') : array 31 | { 32 | $result = []; 33 | 34 | foreach ($array as $key => $value) { 35 | if (\is_array($value) && \count($value) > 1) { 36 | $result += $this->flatterArray($value, $prefix . $key . '.'); 37 | } else { 38 | $result[$prefix . $key] = \is_array($value) ? \current($value) : $value; 39 | } 40 | } 41 | 42 | return $result; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 App Insights PHP 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Listener/ExceptionListener.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Listener; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 18 | use Symfony\Component\HttpKernel\Event\ExceptionEvent; 19 | use Symfony\Component\HttpKernel\KernelEvents; 20 | 21 | final class ExceptionListener implements EventSubscriberInterface 22 | { 23 | private $telemetryClient; 24 | 25 | private $exceptionLogged; 26 | 27 | public function __construct(Client $telemetryClient) 28 | { 29 | $this->telemetryClient = $telemetryClient; 30 | $this->exceptionLogged = false; 31 | } 32 | 33 | public static function getSubscribedEvents() 34 | { 35 | return [ 36 | KernelEvents::EXCEPTION => ['onException', 1000], 37 | ]; 38 | } 39 | 40 | public function onException(ExceptionEvent $event) : void 41 | { 42 | if (!$this->telemetryClient->getContext()->getInstrumentationKey()) { 43 | // instrumentation key is emtpy 44 | return; 45 | } 46 | 47 | if (!$this->telemetryClient->configuration()->exceptions()->isEnabled()) { 48 | return; 49 | } 50 | 51 | if ($this->exceptionLogged) { 52 | return; 53 | } 54 | 55 | $this->telemetryClient->trackException($event->getThrowable()); 56 | $this->exceptionLogged = true; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Listener/HttpRequestListener.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Listener; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\FlatArray; 18 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 19 | use Symfony\Component\HttpKernel\Event\KernelEvent; 20 | use Symfony\Component\HttpKernel\Event\RequestEvent; 21 | use Symfony\Component\HttpKernel\Event\ResponseEvent; 22 | use Symfony\Component\HttpKernel\KernelEvents; 23 | 24 | final class HttpRequestListener implements EventSubscriberInterface 25 | { 26 | private $telemetryClient; 27 | 28 | private $request; 29 | 30 | private $requestStartTime; 31 | 32 | private $requestStartTimeMs; 33 | 34 | public function __construct(Client $telemetryClient) 35 | { 36 | $this->telemetryClient = $telemetryClient; 37 | } 38 | 39 | public static function getSubscribedEvents() 40 | { 41 | return [ 42 | KernelEvents::REQUEST => ['onKernelRequest', 1000], 43 | KernelEvents::RESPONSE => ['onKernelResponse', -1000], 44 | ]; 45 | } 46 | 47 | public function onKernelRequest(RequestEvent $event) : void 48 | { 49 | if (!$this->isMainRequest($event)) { 50 | return; 51 | } 52 | 53 | if (!$this->telemetryClient->configuration()->requests()->isEnabled()) { 54 | return; 55 | } 56 | 57 | if (!$this->telemetryClient->getContext()->getInstrumentationKey()) { 58 | // instrumentation key is emtpy 59 | return; 60 | } 61 | 62 | $this->requestStartTime = \time(); 63 | $this->requestStartTimeMs = (int) \round(\microtime(true) * 1000, 1); 64 | 65 | $request = $event->getRequest(); 66 | 67 | $this->telemetryClient->getContext()->getLocationContext()->setIp($request->getClientIp()); 68 | 69 | if ($request->hasSession()) { 70 | $this->telemetryClient->getContext()->getSessionContext()->setId($request->getSession()->getId()); 71 | } 72 | 73 | $this->telemetryClient->getContext()->getOperationContext()->setName($request->getMethod() . ' ' . $request->getPathInfo()); 74 | 75 | $this->request = $this->telemetryClient->beginRequest( 76 | $request->getMethod() . ' ' . $request->getPathInfo(), 77 | $request->getUriForPath($request->getPathInfo()), 78 | $this->requestStartTime 79 | ); 80 | } 81 | 82 | public function onKernelResponse(ResponseEvent $event) : void 83 | { 84 | if (!$this->isMainRequest($event)) { 85 | return; 86 | } 87 | 88 | if (!$this->telemetryClient->configuration()->requests()->isEnabled()) { 89 | return; 90 | } 91 | 92 | if (!$this->telemetryClient->getContext()->getInstrumentationKey()) { 93 | // instrumentation key is empty 94 | return; 95 | } 96 | 97 | $request = $event->getRequest(); 98 | $response = $event->getResponse(); 99 | 100 | $this->telemetryClient->endRequest( 101 | $this->request, 102 | (int) \round(\microtime(true) * 1000, 1) - $this->requestStartTimeMs, 103 | $response->getStatusCode(), 104 | $response->isSuccessful() || $response->isRedirection(), 105 | (new FlatArray([ 106 | 'headers' => [ 107 | 'accept-language' => $request->headers->get('accept-language'), 108 | 'accept-encoding' => $request->headers->get('accept-encoding'), 109 | 'accept' => $request->headers->get('accept'), 110 | 'user-agent' => $request->headers->get('user-agent'), 111 | 'host' => $request->headers->get('host'), 112 | ], 113 | 'query' => $request->query->all(), 114 | 'clientIps' => $request->getClientIps(), 115 | ]))() 116 | ); 117 | } 118 | 119 | private function isMainRequest(KernelEvent $event) : bool 120 | { 121 | return \method_exists($event, 'isMainRequest') 122 | ? $event->isMainRequest() 123 | : $event->isMasterRequest(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Listener/KernelTerminateListener.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Listener; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use Symfony\Component\Console\ConsoleEvents; 18 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 19 | use Symfony\Component\HttpKernel\KernelEvents; 20 | 21 | final class KernelTerminateListener implements EventSubscriberInterface 22 | { 23 | private $telemetryClient; 24 | 25 | public function __construct(Client $telemetryClient) 26 | { 27 | $this->telemetryClient = $telemetryClient; 28 | } 29 | 30 | public static function getSubscribedEvents() 31 | { 32 | $listeners = [ 33 | KernelEvents::TERMINATE => ['onTerminate', -1000], 34 | ]; 35 | 36 | if (\class_exists('Symfony\Component\Console\ConsoleEvents')) { 37 | $listeners[ConsoleEvents::TERMINATE] = 'onTerminate'; 38 | } 39 | 40 | return $listeners; 41 | } 42 | 43 | public function onTerminate() : void 44 | { 45 | if (!$this->telemetryClient->getContext()->getInstrumentationKey()) { 46 | // instrumentation key is empty 47 | return; 48 | } 49 | 50 | if (!\count($this->telemetryClient->getChannel()->getQueue())) { 51 | // telemetry client queue is empty 52 | return; 53 | } 54 | 55 | $this->telemetryClient->flush(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AppInsightsPHPBundle 2 | ============= 3 | 4 | The AppInsightsPHPBundle brings support for Microsoft Application Insights into Symfony 3.4+ applications. 5 | It's a profiler you can use on production to track everything that is important for you and your system. 6 | 7 | [![Latest Stable Version](https://poser.pugx.org/app-insights-php/app-insights-php-bundle/v/stable)](https://packagist.org/packages/app-insights-php/app-insights-php-bundle) 8 | [![Total Downloads](https://poser.pugx.org/app-insights-php/app-insights-php-bundle/downloads)](https://packagist.org/packages/app-insights-php/app-insights-php-bundle) 9 | [![Latest Unstable Version](https://poser.pugx.org/app-insights-php/app-insights-php-bundle/v/unstable)](https://packagist.org/packages/app-insights-php/app-insights-php-bundle) 10 | [![License](https://poser.pugx.org/app-insights-php/app-insights-php-bundle/license)](https://packagist.org/packages/app-insights-php/app-insights-php-bundle) 11 | 12 | 13 | Microsoft App Insights allows you to track on production following metrics 14 | 15 | * Traces (Logs with different verbosity level) 16 | * Requests (http requests served by your app) 17 | * Custom Events (web/cli/javascript) 18 | * Dependencies (SQL, Elasticsearch, Redis - any 3rd party service/system/api) 19 | * Exceptions (web/cli/javascript) 20 | * PageViews (javascript) 21 | 22 | Query logs, visualize metrics and pin them to Azure Portal Dashboard, create alerts from metrics & health checks 23 | 24 | All you need to do is register free [Microsoft Azure Account](https://azure.microsoft.com/en-us/free/free-account-faq/), 25 | setup new [App Insights Instance](https://docs.microsoft.com/en-us/azure/azure-monitor/app/create-new-resource) and install 26 | this bundle in you symfony app. 27 | 28 | This bundle integrates app insights with all important libraries used by most of Symfony based applications. 29 | 30 | * Monolog Handler (Trace) 31 | * Doctrine Logger (Dependency) 32 | * Symfony HTTP (Request) 33 | * Symfony Exception (Exceptions) 34 | 35 | Microsoft App Insights is perfect for small teams that can't afford expensive monitoring tools or don't 36 | have enough resources to install, configure and maintain powerful open source alternatives. 37 | 38 | If you are looking for a SAAS alternative to: 39 | 40 | * Graylog / Kibana 41 | * Zabbix 42 | * Grafana 43 | * New Relic / Datadog / etc 44 | * Google Analytics 45 | 46 | With 90 days data retention period for ~2.5EUR per GB [Pricing](https://azure.microsoft.com/en-us/pricing/details/monitor/) 47 | First 5GB are free for 31 days.. 48 | Microsoft App Insights is exactly what you need. 49 | 50 | This bundle simplifies App Insights integration with your new or existing project. 51 | 52 | ![Image](https://docs.microsoft.com/en-us/azure/azure-monitor/app/media/web-monitor-performance/performancetriageview7dayszoomedtrendzoomed95th99th.png) 53 | 54 | Documentation 55 | ------------- 56 | 57 | The source of the documentation is stored in the `Resources/doc/` folder 58 | in this bundle, and available on symfony.com: 59 | 60 | [Read the Documentation for master](Resources/doc/index.md) 61 | 62 | Installation 63 | ------------ 64 | 65 | All the installation instructions are located in the documentation. 66 | 67 | License 68 | ------- 69 | 70 | This bundle is under the MIT license. See the complete license [in the bundle](LICENSE) 71 | -------------------------------------------------------------------------------- /Resources/config/app_insights_php.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use AppInsightsPHP\Client\Client; 15 | use AppInsightsPHP\Client\ClientFactory; 16 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 17 | use Symfony\Component\DependencyInjection\Reference; 18 | 19 | return static function (ContainerConfigurator $containerConfigurator) : void { 20 | $services = $containerConfigurator->services(); 21 | 22 | $services->set('app_insights_php.telemetry.factory', ClientFactory::class) 23 | ->args(['%app_insights_php.instrumentation_key%', '', '', '']); 24 | 25 | $services->set('app_insights_php.telemetry', Client::class) 26 | ->public() 27 | ->factory([new Reference('app_insights_php.telemetry.factory'), 'create']); 28 | }; 29 | -------------------------------------------------------------------------------- /Resources/config/app_insights_php_console.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackDependencyCommand; 15 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackEventCommand; 16 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackExceptionCommand; 17 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackMetricCommand; 18 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 19 | use Symfony\Component\DependencyInjection\Reference; 20 | 21 | return static function (ContainerConfigurator $containerConfigurator) : void { 22 | $services = $containerConfigurator->services(); 23 | 24 | $services->set('app_insights_php.symfony.command.track_dependency', TrackDependencyCommand::class) 25 | ->tag('console.command', []) 26 | ->args([new Reference('app_insights_php.telemetry')]); 27 | 28 | $services->set('app_insights_php.symfony.command.track_metric', TrackMetricCommand::class) 29 | ->tag('console.command', []) 30 | ->args([new Reference('app_insights_php.telemetry')]); 31 | 32 | $services->set('app_insights_php.symfony.command.track_event', TrackEventCommand::class) 33 | ->tag('console.command', []) 34 | ->args([new Reference('app_insights_php.telemetry')]); 35 | 36 | $services->set('app_insights_php.symfony.command.track_exception', TrackExceptionCommand::class) 37 | ->tag('console.command', []) 38 | ->args([new Reference('app_insights_php.telemetry')]); 39 | }; 40 | -------------------------------------------------------------------------------- /Resources/config/app_insights_php_doctrine.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use AppInsightsPHP\Doctrine\DBAL\Logging\DependencyLogger; 15 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | 18 | return static function (ContainerConfigurator $containerConfigurator) : void { 19 | $services = $containerConfigurator->services(); 20 | 21 | $services->set('app_insights_php.doctrine.logger.dependency', DependencyLogger::class) 22 | ->args([new Reference('app_insights_php.telemetry')]); 23 | }; 24 | -------------------------------------------------------------------------------- /Resources/config/app_insights_php_monolog.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use AppInsightsPHP\Monolog\Handler\AppInsightsTraceHandler; 15 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | 18 | return static function (ContainerConfigurator $containerConfigurator) : void { 19 | $services = $containerConfigurator->services(); 20 | 21 | $services->set('app_insights_php.monolog.handler.app_insights.trace', AppInsightsTraceHandler::class) 22 | ->args([new Reference('app_insights_php.telemetry')]); 23 | }; 24 | -------------------------------------------------------------------------------- /Resources/config/app_insights_php_symfony.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Listener\ExceptionListener; 15 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Listener\HttpRequestListener; 16 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Listener\KernelTerminateListener; 17 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | 20 | return static function (ContainerConfigurator $containerConfigurator) : void { 21 | $services = $containerConfigurator->services(); 22 | 23 | $services->set('app_insights_php.symfony.listener.http_request', HttpRequestListener::class) 24 | ->tag('kernel.event_subscriber', []) 25 | ->args([new Reference('app_insights_php.telemetry')]); 26 | 27 | $services->set('app_insights_php.symfony.listener.kernel_terminate', KernelTerminateListener::class) 28 | ->tag('kernel.event_subscriber', []) 29 | ->args([new Reference('app_insights_php.telemetry')]); 30 | 31 | $services->set('app_insights_php.symfony.listener.exception', ExceptionListener::class) 32 | ->tag('kernel.event_subscriber', []) 33 | ->args([new Reference('app_insights_php.telemetry'), []]); 34 | }; 35 | -------------------------------------------------------------------------------- /Resources/config/app_insights_php_twig.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Twig\TelemetryExtension; 15 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | 18 | return static function (ContainerConfigurator $containerConfigurator) : void { 19 | $services = $containerConfigurator->services(); 20 | 21 | $services->set('app_insights_php.twig.telemetry', TelemetryExtension::class) 22 | ->tag('twig.extension', []) 23 | ->args([new Reference('app_insights_php.telemetry')]); 24 | }; 25 | -------------------------------------------------------------------------------- /Resources/doc/dependencies.md: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | 3 | Tracking dependencies might be really useful, App Insights will build Application Map from your 4 | dependencies and will track performance degradation for your automatically. 5 | 6 | ![Image](https://docs.microsoft.com/en-us/azure/azure-monitor/app/media/app-map/application-map-001.png) 7 | 8 | ## Disable dependencies tracking 9 | Disabling app insights in long running CLI commands could help you to save extra money and reduce some noise 10 | in your logs. For example if you are reindexing your whole database in elasticsearch tracking each 11 | call to dependency (elasticsearch) is probably useless, it wont give you any valuable data and might slow 12 | down the process and generate extra costs. 13 | 14 | In this case you can disable whole App Insights client or just specific metric type 15 | 16 | ```php 17 | configuration()->disable(); 21 | ``` 22 | 23 | Above code will prevent App Insights PHP Client from tracking any metrics but if for example 24 | you want to disable dependency tracking (indexing documents in elasticsearch) but still need 25 | to track exceptions or critical logs you might want to use following code: 26 | 27 | ```php 28 | configuration()->dependencies()->disable(); 32 | ``` 33 | 34 | Still not satisfied? If for any reason you would like to disable only specific dependency you 35 | can also tell Telemetryt Client to ignore them by name. 36 | 37 | Below example shows how you can ignore Doctrine DBAL SQL dependencies only: 38 | 39 | ```php 40 | configuration() 44 | ->dependencies() 45 | ->ignore(\AppInsightsPHP\Doctrine\DBAL\Logging\DependencyLogger::DEFAULT_NAME); 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /Resources/doc/how_it_works.md: -------------------------------------------------------------------------------- 1 | # How it works 2 | 3 | All components from [App Insights PHP](https://github.com/app-insights-php) are build on top of 4 | official [Microsoft App Insights SDK](https://github.com/Microsoft/ApplicationInsights-PHP) for PHP. Official SDK talks to App Insights API through HTTP protocol. 5 | 6 | What about performance? This bundle should not affect your system performance at all however 7 | there are some best practices we recommend you to follow (more later). 8 | 9 | Basically whole idea behind official SDK is to collect all metrics/logs in memory and flush them 10 | when needed. 11 | 12 | ```php 13 | trackEvent('My Custom Event!'); 18 | 19 | $redisStartTime = time(); 20 | 21 | $appInsights->doSomething(); 22 | 23 | $appInsights->trackDependency('Redis', 'Cache', 'do something()', $redisStartTime); 24 | ``` 25 | 26 | In above example AppInsights client will track custom event and redis call (dependency) however 27 | it will not fire any HTTP call, it needs to be done explicitly by calling `flush()` method. 28 | 29 | ```php 30 | flush(); 33 | 34 | ``` 35 | 36 | `AppInsightsPHPBundle` makes use of Symfony [kernel.terminate](https://symfony.com/doc/current/reference/events.html#kernel-terminate) event 37 | to flush SDK telemetry queue. 38 | 39 | 40 | Read more about tracking: 41 | 42 | * [Dependencies](dependencies.md) 43 | * [Traces](traces.md) 44 | * [PageViews](page_views.md) -------------------------------------------------------------------------------- /Resources/doc/index.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Supported symfony versions: 4 | 5 | * `>= 3.4` 6 | * `>= 4.0` 7 | 8 | ## Applications that don't use Symfony Flex 9 | 10 | ### Step 1: Download the Bundle 11 | 12 | Open a command console, enter your project directory and execute the 13 | following command to download the latest stable version of this bundle: 14 | 15 | ```console 16 | $ composer require app-insights-php/app-insights-php-bundle 17 | ``` 18 | 19 | This command requires you to have Composer installed globally, as explained 20 | in the [installation chapter](https://getcomposer.org/doc/00-intro.md) 21 | of the Composer documentation. 22 | 23 | ## Applications that use Symfony Flex 24 | 25 | (**Not available yet**) 26 | 27 | Open a command console, enter your project directory and execute: 28 | 29 | ```console 30 | $ composer require app-insights-php 31 | ``` 32 | 33 | ### Step 2: Enable the Bundle 34 | 35 | Then, enable the bundle by adding it to the list of registered bundles 36 | in the `app/AppKernel.php` file of your project: 37 | 38 | ```php 39 | 11 | 12 | 13 | ... 14 | {{ app_insights_php() }} 15 | 16 | 17 | ... 18 | 19 | 20 | ``` 21 | 22 | If you want to identify your metrics by logged users you can also pass username/userid 23 | optional id to this function 24 | 25 | ```html 26 | {% if app.user %} 27 | {{ app_insights_php(app.user.username) }} 28 | {% else %} 29 | {{ app_insights_php() }} 30 | {% endif %} 31 | ``` -------------------------------------------------------------------------------- /Resources/doc/traces.md: -------------------------------------------------------------------------------- 1 | # Traces 2 | 3 | Traces in App Insights are nothing else than good known to everyone in PHP community Logs (Monolog). 4 | 5 | You can use one of following trace level: 6 | 7 | ``` 8 | abstract class Severity_Level 9 | { 10 | const Verbose = 0; 11 | const Information = 1; 12 | const Warning = 2; 13 | const Error = 3; 14 | const Critical = 4; 15 | } 16 | ``` 17 | 18 | [Our monolog hanlder](https://github.com/app-insights-php/monolog-handler) brings for you out of the 19 | box integration with app insights. This means that if your system is using monolog stream logger you 20 | can easily replace it with App Insights PHP trace logger to start sending your logs into the cloud. 21 | 22 | ## Optimize Memory Usage 23 | 24 | This is especially problematic when you log things in lon running CLI command. In order to 25 | avoid memory leaks you should be using [Buffer Handler](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/BufferHandler.php) 26 | in from of App Insights PHP trace handler. 27 | 28 | ## Configuration 29 | 30 | ```yaml 31 | app_insights_php: 32 | monolog: 33 | handlers: 34 | trace: # app_insights_php.monolog.handler.trace 35 | type: trace # trace | dependency 36 | level: ERROR 37 | bubble: true 38 | dependency: # app_insights_php.monolog.handler.dependency 39 | type: dependency # trace | dependency 40 | ``` 41 | 42 | Above configuration will register in service container trace handler with `app_insights_php.monolog.handler.trace` id. 43 | You can use it later directly in your monolog bundle configuration: 44 | 45 | ```yaml 46 | monolog: 47 | handlers: 48 | logger_buffer: 49 | type: buffer 50 | buffer_size: 100 51 | handler: app_insights 52 | level: error 53 | app_insights: 54 | type: service 55 | id: "app_insights_php.monolog.handler.trace" 56 | ``` 57 | 58 | ### Types 59 | 60 | `trace` 61 | 62 | Regular handler with few log levels, should be used in most cases. 63 | 64 | `dependency` 65 | 66 | Special handler useful when you are logging requests/responses from external 67 | services/systems/apis. 68 | Instead of using `trackTrace` this handler will use `trackDependency` method 69 | on App Insights PHP Client. 70 | 71 | **Known limitations** 72 | 73 | There is no good and easy way to track dependency start and duration time :| 74 | Those fields are going to be null. 75 | -------------------------------------------------------------------------------- /Tests/Command/TrackDependencyCommandTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Client\Configuration; 18 | use AppInsightsPHP\Client\FailureCache; 19 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackDependencyCommand; 20 | use ApplicationInsights\Channel\Telemetry_Channel; 21 | use ApplicationInsights\Telemetry_Client; 22 | use GuzzleHttp\Psr7\Response; 23 | use PHPUnit\Framework\TestCase; 24 | use Psr\Log\NullLogger; 25 | use Psr\SimpleCache\CacheInterface; 26 | use Symfony\Component\Console\Application; 27 | use Symfony\Component\Console\Tester\CommandTester; 28 | 29 | final class TrackDependencyCommandTest extends TestCase 30 | { 31 | public function test_tracking_dependency() : void 32 | { 33 | $telemetryClientMock = $this->createMock(Telemetry_Client::class); 34 | $telemetryClientMock->method('getChannel')->willReturn($telemetryChannelMock = $this->createMock(Telemetry_Channel::class)); 35 | 36 | $client = new Client( 37 | $telemetryClientMock, 38 | Configuration::createDefault(), 39 | new FailureCache($this->createMock(CacheInterface::class)), 40 | new NullLogger() 41 | ); 42 | 43 | $application = new Application(); 44 | $application->add(new TrackDependencyCommand($client)); 45 | 46 | $tester = new CommandTester($application->get(TrackDependencyCommand::NAME)); 47 | 48 | $telemetryClientMock->expects($this->once()) 49 | ->method('trackDependency') 50 | ->with('Dependency Name', 'SQL', 'veryComplexQuery', $this->greaterThan(0), 10, true, null, null); 51 | 52 | $telemetryClientMock->expects($this->once()) 53 | ->method('flush') 54 | ->willReturn(new Response()); 55 | 56 | $result = $tester->execute([ 57 | 'name' => 'Dependency Name', 58 | '--type' => 'SQL', 59 | '--commandName' => 'veryComplexQuery', 60 | '--durationTime' => 10, 61 | ]); 62 | 63 | $this->assertEquals($result, 0); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/Command/TrackEventCommandTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Client\Configuration; 18 | use AppInsightsPHP\Client\FailureCache; 19 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackEventCommand; 20 | use ApplicationInsights\Channel\Telemetry_Channel; 21 | use ApplicationInsights\Telemetry_Client; 22 | use GuzzleHttp\Psr7\Response; 23 | use PHPUnit\Framework\TestCase; 24 | use Psr\Log\NullLogger; 25 | use Psr\SimpleCache\CacheInterface; 26 | use Symfony\Component\Console\Application; 27 | use Symfony\Component\Console\Tester\CommandTester; 28 | 29 | final class TrackEventCommandTest extends TestCase 30 | { 31 | public function test_tracking_event() : void 32 | { 33 | $telemetryClientMock = $this->createMock(Telemetry_Client::class); 34 | $telemetryClientMock->method('getChannel')->willReturn($telemetryChannelMock = $this->createMock(Telemetry_Channel::class)); 35 | 36 | $client = new Client( 37 | $telemetryClientMock, 38 | Configuration::createDefault(), 39 | new FailureCache($this->createMock(CacheInterface::class)), 40 | new NullLogger() 41 | ); 42 | 43 | $application = new Application(); 44 | $application->add(new TrackEventCommand($client)); 45 | 46 | $tester = new CommandTester($application->get(TrackEventCommand::NAME)); 47 | 48 | $telemetryClientMock->expects($this->once()) 49 | ->method('trackEvent') 50 | ->with('Event Name', ['property' => 1], ['measurement' => 2]); 51 | 52 | $telemetryClientMock->expects($this->once()) 53 | ->method('flush') 54 | ->willReturn(new Response()); 55 | 56 | $result = $tester->execute([ 57 | 'name' => 'Event Name', 58 | '--properties' => '{"property":1}', 59 | '--measurements' => '{"measurement":2}', 60 | ]); 61 | 62 | $this->assertEquals($result, 0); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tests/Command/TrackExceptionCommandTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Client\Configuration; 18 | use AppInsightsPHP\Client\FailureCache; 19 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackExceptionCommand; 20 | use ApplicationInsights\Channel\Telemetry_Channel; 21 | use ApplicationInsights\Telemetry_Client; 22 | use GuzzleHttp\Psr7\Response; 23 | use PHPUnit\Framework\TestCase; 24 | use Psr\Log\NullLogger; 25 | use Psr\SimpleCache\CacheInterface; 26 | use Symfony\Component\Console\Application; 27 | use Symfony\Component\Console\Tester\CommandTester; 28 | 29 | final class TrackExceptionCommandTest extends TestCase 30 | { 31 | public function test_tracking_metric() : void 32 | { 33 | $telemetryClientMock = $this->createMock(Telemetry_Client::class); 34 | $telemetryClientMock->method('getChannel')->willReturn($telemetryChannelMock = $this->createMock(Telemetry_Channel::class)); 35 | 36 | $client = new Client( 37 | $telemetryClientMock, 38 | Configuration::createDefault(), 39 | new FailureCache($this->createMock(CacheInterface::class)), 40 | new NullLogger() 41 | ); 42 | 43 | $application = new Application(); 44 | $application->add(new TrackexceptionCommand($client)); 45 | 46 | $tester = new CommandTester($application->get(TrackexceptionCommand::NAME)); 47 | 48 | $telemetryClientMock->expects($this->once()) 49 | ->method('trackException') 50 | ->with(new \Exception('')); 51 | 52 | $telemetryClientMock->expects($this->once()) 53 | ->method('flush') 54 | ->willReturn(new Response()); 55 | 56 | $result = $tester->execute([]); 57 | 58 | $this->assertEquals($result, 0); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tests/Command/TrackMetricCommandTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\Command; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Client\Configuration; 18 | use AppInsightsPHP\Client\FailureCache; 19 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Command\TrackMetricCommand; 20 | use ApplicationInsights\Channel\Telemetry_Channel; 21 | use ApplicationInsights\Telemetry_Client; 22 | use GuzzleHttp\Psr7\Response; 23 | use PHPUnit\Framework\TestCase; 24 | use Psr\Log\NullLogger; 25 | use Psr\SimpleCache\CacheInterface; 26 | use Symfony\Component\Console\Application; 27 | use Symfony\Component\Console\Tester\CommandTester; 28 | 29 | final class TrackMetricCommandTest extends TestCase 30 | { 31 | public function test_tracking_metric() : void 32 | { 33 | $telemetryClientMock = $this->createMock(Telemetry_Client::class); 34 | $telemetryClientMock->method('getChannel')->willReturn($telemetryChannelMock = $this->createMock(Telemetry_Channel::class)); 35 | 36 | $client = new Client( 37 | $telemetryClientMock, 38 | Configuration::createDefault(), 39 | new FailureCache($this->createMock(CacheInterface::class)), 40 | new NullLogger() 41 | ); 42 | 43 | $application = new Application(); 44 | $application->add(new TrackMetricCommand($client)); 45 | 46 | $tester = new CommandTester($application->get(TrackMetricCommand::NAME)); 47 | 48 | $telemetryClientMock->expects($this->once()) 49 | ->method('trackMetric') 50 | ->with('Metric Name', '123', 0); 51 | 52 | $telemetryClientMock->expects($this->once()) 53 | ->method('flush') 54 | ->willReturn(new Response()); 55 | 56 | $result = $tester->execute([ 57 | 'name' => 'Metric Name', 58 | 'value' => '123', 59 | '--type' => 0, 60 | ]); 61 | 62 | $this->assertEquals($result, 0); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tests/DependencyInjection/AppInsightsPHPExtensionTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\DependencyInjection; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Doctrine\DBAL\Logging\DependencyLogger; 18 | use AppInsightsPHP\Monolog\Handler\AppInsightsTraceHandler; 19 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\DependencyInjection\AppInsightsPHPExtension; 20 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Twig\TelemetryExtension; 21 | use Monolog\Logger; 22 | use PHPUnit\Framework\TestCase; 23 | use Symfony\Component\DependencyInjection\ContainerBuilder; 24 | use Symfony\Component\HttpKernel\KernelInterface; 25 | 26 | final class AppInsightsPHPExtensionTest extends TestCase 27 | { 28 | private $kernel; 29 | 30 | /** 31 | * @var \Symfony\Component\DependencyInjection\ContainerBuilder 32 | */ 33 | private $container; 34 | 35 | protected function setUp() : void 36 | { 37 | parent::setUp(); 38 | 39 | $this->kernel = $this->getMockBuilder(KernelInterface::class)->getMock(); 40 | $this->container = new ContainerBuilder(); 41 | } 42 | 43 | protected function tearDown() : void 44 | { 45 | parent::tearDown(); 46 | 47 | $this->container = null; 48 | $this->kernel = null; 49 | } 50 | 51 | public function test_default_configuration() : void 52 | { 53 | $extension = new AppInsightsPHPExtension(); 54 | $extension->load( 55 | [[ 56 | 'instrumentation_key' => 'test_key', 57 | ]], 58 | $this->container 59 | ); 60 | 61 | $this->assertTrue($this->container->hasDefinition('app_insights_php.telemetry')); 62 | $this->assertTrue($this->container->hasParameter('app_insights_php.instrumentation_key')); 63 | 64 | $this->assertFalse($this->container->hasDefinition('app_insights_php.doctrine.logger.app_insights')); 65 | 66 | $this->assertTrue($this->container->get('app_insights_php.configuration')->isEnabled()); 67 | $this->assertFalse($this->container->get('app_insights_php.configuration')->gzipEnabled()); 68 | $this->assertTrue($this->container->get('app_insights_php.configuration.exceptions')->isEnabled()); 69 | $this->assertTrue($this->container->get('app_insights_php.configuration.traces')->isEnabled()); 70 | $this->assertTrue($this->container->get('app_insights_php.configuration.dependencies')->isEnabled()); 71 | $this->assertTrue($this->container->get('app_insights_php.configuration.requests')->isEnabled()); 72 | 73 | $this->assertTrue($this->container->hasDefinition('app_insights_php.symfony.listener.http_request')); 74 | $this->assertTrue($this->container->hasDefinition('app_insights_php.symfony.listener.kernel_terminate')); 75 | $this->assertTrue($this->container->hasDefinition('app_insights_php.symfony.listener.exception')); 76 | 77 | $this->assertInstanceOf(Client::class, $this->container->get('app_insights_php.telemetry')); 78 | } 79 | 80 | public function test_configuration_when_enabled_is_set_to_false() : void 81 | { 82 | $extension = new AppInsightsPHPExtension(); 83 | $extension->load( 84 | [[ 85 | 'enabled' => false, 86 | 'instrumentation_key' => 'test_key', 87 | ]], 88 | $this->container 89 | ); 90 | 91 | $this->assertTrue($this->container->hasDefinition('app_insights_php.telemetry')); 92 | $this->assertTrue($this->container->hasParameter('app_insights_php.instrumentation_key')); 93 | 94 | $this->assertFalse($this->container->hasDefinition('app_insights_php.doctrine.logger.app_insights')); 95 | 96 | $this->assertFalse($this->container->get('app_insights_php.configuration')->isEnabled()); 97 | $this->assertTrue($this->container->get('app_insights_php.configuration.exceptions')->isEnabled()); 98 | $this->assertTrue($this->container->get('app_insights_php.configuration.traces')->isEnabled()); 99 | $this->assertTrue($this->container->get('app_insights_php.configuration.dependencies')->isEnabled()); 100 | $this->assertTrue($this->container->get('app_insights_php.configuration.requests')->isEnabled()); 101 | 102 | $this->assertFalse($this->container->hasDefinition('app_insights_php.symfony.listener.http_request')); 103 | $this->assertFalse($this->container->hasDefinition('app_insights_php.symfony.listener.kernel_terminate')); 104 | $this->assertFalse($this->container->hasDefinition('app_insights_php.symfony.listener.exception')); 105 | 106 | $this->assertInstanceOf(Client::class, $this->container->get('app_insights_php.telemetry')); 107 | } 108 | 109 | public function test_fallback_logger_configuration() : void 110 | { 111 | $extension = new AppInsightsPHPExtension(); 112 | $extension->load( 113 | [[ 114 | 'instrumentation_key' => 'test_key', 115 | 'fallback_logger' => [ 116 | 'service_id' => 'logger', 117 | ], 118 | ]], 119 | $this->container 120 | ); 121 | 122 | $this->assertSame( 123 | 'logger', 124 | (string) $this->container->getDefinition('app_insights_php.telemetry.factory')->getArgument(3) 125 | ); 126 | } 127 | 128 | public function test_fallback_logger_with_monolog_channel_configuration() : void 129 | { 130 | $extension = new AppInsightsPHPExtension(); 131 | $extension->load( 132 | [[ 133 | 'instrumentation_key' => 'test_key', 134 | 'fallback_logger' => [ 135 | 'service_id' => 'logger', 136 | 'monolog_channel' => 'main', 137 | ], 138 | ]], 139 | $this->container 140 | ); 141 | 142 | $this->assertSame( 143 | 'logger', 144 | (string) $this->container->getDefinition('app_insights_php.telemetry.factory')->getArgument(3) 145 | ); 146 | $this->assertSame( 147 | 'main', 148 | $this->container->getDefinition('app_insights_php.telemetry.factory')->getTag('monolog.logger')[0]['channel'] 149 | ); 150 | } 151 | 152 | public function test_failure_cache_configuration() : void 153 | { 154 | $extension = new AppInsightsPHPExtension(); 155 | $extension->load( 156 | [[ 157 | 'instrumentation_key' => 'test_key', 158 | 'failure_cache_service_id' => 'failure_cache_id', 159 | ]], 160 | $this->container 161 | ); 162 | 163 | $this->assertSame( 164 | 'failure_cache_id', 165 | (string) $this->container->getDefinition('app_insights_php.telemetry.factory')->getArgument(2) 166 | ); 167 | } 168 | 169 | public function test_doctrine_logger_configuration() : void 170 | { 171 | $extension = new AppInsightsPHPExtension(); 172 | $extension->load( 173 | [[ 174 | 'instrumentation_key' => 'test_key', 175 | 'doctrine' => [ 176 | 'track_dependency' => true, 177 | ], 178 | ]], 179 | $this->container 180 | ); 181 | 182 | $this->assertTrue($this->container->hasDefinition('app_insights_php.doctrine.logger.dependency')); 183 | $this->assertInstanceOf(DependencyLogger::class, $this->container->get('app_insights_php.doctrine.logger.dependency')); 184 | } 185 | 186 | public function test_twig_configuration() : void 187 | { 188 | $extension = new AppInsightsPHPExtension(); 189 | $extension->load( 190 | [[ 191 | 'instrumentation_key' => 'test_key', 192 | ]], 193 | $this->container 194 | ); 195 | 196 | $this->assertInstanceOf(TelemetryExtension::class, $this->container->get('app_insights_php.twig.telemetry')); 197 | } 198 | 199 | public function test_ignored_exceptions_configuration() : void 200 | { 201 | $extension = new AppInsightsPHPExtension(); 202 | $extension->load( 203 | [[ 204 | 'instrumentation_key' => 'test_key', 205 | 'exceptions' => [ 206 | 'ignored_exceptions' => [\RuntimeException::class], 207 | ], 208 | ]], 209 | $this->container 210 | ); 211 | 212 | $this->assertTrue($this->container->get('app_insights_php.configuration')->exceptions()->isIgnored(\RuntimeException::class)); 213 | $this->assertFalse($this->container->get('app_insights_php.configuration')->exceptions()->isIgnored(\Exception::class)); 214 | } 215 | 216 | public function test_monolog_configuration() : void 217 | { 218 | $extension = new AppInsightsPHPExtension(); 219 | $extension->load( 220 | [[ 221 | 'instrumentation_key' => 'test_key', 222 | 'monolog' => [ 223 | 'handlers' => [ 224 | [ 225 | 'name' => 'foo.logger', 226 | 'level' => Logger::DEBUG, 227 | ], 228 | ], 229 | ], 230 | ]], 231 | $this->container 232 | ); 233 | 234 | $this->assertInstanceOf(AppInsightsTraceHandler::class, $this->container->get('app_insights_php.monolog.handler.foo.logger')); 235 | } 236 | 237 | public function test_gzip_configuration() : void 238 | { 239 | $extension = new AppInsightsPHPExtension(); 240 | $extension->load( 241 | [[ 242 | 'instrumentation_key' => 'test_key', 243 | 'gzip_enabled' => true, 244 | ]], 245 | $this->container 246 | ); 247 | 248 | $this->assertTrue($this->container->get('app_insights_php.configuration')->gzipEnabled()); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Tests/DependencyInjection/ConfigurationTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\DependencyInjection; 15 | 16 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\DependencyInjection\Configuration; 17 | use Monolog\Logger; 18 | use PHPUnit\Framework\TestCase; 19 | use Symfony\Component\Config\Definition\Processor; 20 | 21 | final class ConfigurationTest extends Testcase 22 | { 23 | public function test_default_configuration() : void 24 | { 25 | $configs = [ 26 | [ 27 | 'instrumentation_key' => 'test_key', 28 | ], 29 | ]; 30 | 31 | $config = $this->process($configs); 32 | 33 | $this->assertEquals('test_key', $config['instrumentation_key']); 34 | $this->assertNull($config['failure_cache_service_id']); 35 | $this->assertTrue($config['enabled']); 36 | $this->assertFalse($config['gzip_enabled']); 37 | $this->assertEquals([], $config['exceptions']['ignored_exceptions']); 38 | $this->assertFalse($config['doctrine']['track_dependency']); 39 | $this->assertEmpty($config['monolog']['handlers']); 40 | } 41 | 42 | public function test_monolog_configuration() : void 43 | { 44 | $configs = [ 45 | [ 46 | 'instrumentation_key' => 'test_key', 47 | 'monolog' => [ 48 | 'handlers' => [ 49 | [ 50 | 'type' => 'trace', 51 | 'name' => 'foo.logger', 52 | 'level' => Logger::DEBUG, 53 | ], 54 | [ 55 | 'name' => 'bar.logger', 56 | 'type' => 'dependency', 57 | ], 58 | ], 59 | ], 60 | ], 61 | ]; 62 | 63 | $config = $this->process($configs); 64 | 65 | $this->assertArrayHasKey('foo.logger', $config['monolog']['handlers']); 66 | $this->assertEquals(Logger::DEBUG, $config['monolog']['handlers']['foo.logger']['level']); 67 | $this->assertTrue($config['monolog']['handlers']['foo.logger']['bubble']); 68 | $this->assertEquals('trace', $config['monolog']['handlers']['foo.logger']['type']); 69 | $this->assertEquals('dependency', $config['monolog']['handlers']['bar.logger']['type']); 70 | } 71 | 72 | public function test_gzip_configuration() : void 73 | { 74 | $configs = [ 75 | [ 76 | 'instrumentation_key' => 'test_key', 77 | 'gzip_enabled' => true, 78 | ], 79 | ]; 80 | 81 | $config = $this->process($configs); 82 | 83 | $this->assertTrue($config['gzip_enabled']); 84 | } 85 | 86 | public function test_failure_cache_configuration() : void 87 | { 88 | $configs = [ 89 | [ 90 | 'instrumentation_key' => 'test_key', 91 | 'failure_cache_service_id' => 'failure_cache_id', 92 | ], 93 | ]; 94 | 95 | $config = $this->process($configs); 96 | 97 | $this->assertEquals('failure_cache_id', $config['failure_cache_service_id']); 98 | } 99 | 100 | protected function process($configs) 101 | { 102 | $processor = new Processor(); 103 | 104 | return $processor->processConfiguration(new Configuration(), $configs); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Tests/FlatArrayTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests; 15 | 16 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\FlatArray; 17 | use PHPUnit\Framework\TestCase; 18 | 19 | final class FlatArrayTest extends TestCase 20 | { 21 | public static function arrays() : \Generator 22 | { 23 | yield [[], []]; 24 | yield [ 25 | [ 26 | 'id' => 'user-id', 27 | 'dimensions.length.value' => 10, 28 | 'dimensions.length.uom' => 'inches', 29 | 'dimensions.width.value' => 20, 30 | 'dimensions.width.uom' => 'inches', 31 | 'dimensions.height.value' => 30, 32 | 'dimensions.height.uom' => 'inches', 33 | 'names' => 'John Snow', 34 | 'tags.0' => 'primary', 35 | 'tags.1' => 'default', 36 | 'tags.2' => 'principle', 37 | ], 38 | [ 39 | 'id' => 'user-id', 40 | 'dimensions' => [ 41 | 'length' => [ 42 | 'value' => 10, 43 | 'uom' => 'inches', 44 | ], 45 | 'width' => [ 46 | 'value' => 20, 47 | 'uom' => 'inches', 48 | ], 49 | 'height' => [ 50 | 'value' => 30, 51 | 'uom' => 'inches', 52 | ], 53 | ], 54 | 'names' => ['John Snow'], 55 | 'tags' => ['primary', 'default', 'principle'], 56 | ], 57 | ]; 58 | } 59 | 60 | /** 61 | * @dataProvider arrays 62 | */ 63 | public function test_flat_array(array $flatArray, array $array) : void 64 | { 65 | $this->assertEquals( 66 | $flatArray, 67 | (new FlatArray($array))() 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tests/Functional/AppKernel.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\Functional; 15 | 16 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\AppInsightsPHPBundle; 17 | use Symfony\Bundle\FrameworkBundle\FrameworkBundle; 18 | use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; 19 | use Symfony\Bundle\MonologBundle\MonologBundle; 20 | use Symfony\Component\Config\Loader\LoaderInterface; 21 | use Symfony\Component\DependencyInjection\ContainerBuilder; 22 | use Symfony\Component\HttpKernel\Kernel as BaseKernel; 23 | use Symfony\Component\Routing\RouteCollectionBuilder; 24 | 25 | final class AppKernel extends BaseKernel 26 | { 27 | use MicroKernelTrait; 28 | 29 | public function registerBundles() 30 | { 31 | return [ 32 | new FrameworkBundle(), 33 | new MonologBundle(), 34 | new AppInsightsPHPBundle(), 35 | ]; 36 | } 37 | 38 | public function getCacheDir() 39 | { 40 | return \sys_get_temp_dir() . '/PHPAppInsights/cache'; 41 | } 42 | 43 | public function getLogDir() 44 | { 45 | return \sys_get_temp_dir() . '/PHPAppInsights/logs'; 46 | } 47 | 48 | protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader) : void 49 | { 50 | $configFilePath = __DIR__ . '/config/app_insights_php.php'; 51 | 52 | if (!\file_exists($configFilePath)) { 53 | throw new \RuntimeException('Please create ' . $configFilePath . ' first, use ' . $configFilePath . '.dist as a template'); 54 | } 55 | 56 | $c->loadFromExtension('framework', [ 57 | 'secret' => 'S0ME_SECRET', 58 | ]); 59 | $c->loadFromExtension('app_insights_php', require_once $configFilePath); 60 | $c->loadFromExtension('monolog', [ 61 | 'handlers' => [ 62 | 'file_log' => [ 63 | 'type' => 'stream', 64 | 'path' => '%kernel.logs_dir%/%kernel.environment%.log', 65 | 'level' => 'error', 66 | ], 67 | 'console' => [ 68 | 'type' => 'console', 69 | 'process_psr_3_messages' => false, 70 | 'channels' => ['!event', '!doctrine', '!console'], 71 | ], 72 | ], 73 | ]); 74 | } 75 | 76 | protected function configureRoutes(RouteCollectionBuilder $routes) : void 77 | { 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Tests/Functional/README.md: -------------------------------------------------------------------------------- 1 | # Functional Testing 2 | 3 | This isn't really a place with automated functional tests, it's more a sandbox for AppInsightsPHPBundle that 4 | you can using during this bundle development. 5 | 6 | If you would like to manually test this bundle during development you might want to use [bin/console](bin/console) 7 | CLI Application. 8 | 9 | First prepare [config/app_insights_php.php](config/app_insights_php.php.dist) using `config/app_insights_php.php.dist` template. 10 | If you provide valid App Insights Instrumentation Key you should be able to start using App Insights PHP CLI commands. 11 | 12 | -------------------------------------------------------------------------------- /Tests/Functional/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run($input); -------------------------------------------------------------------------------- /Tests/Functional/config/app_insights_php.php.dist: -------------------------------------------------------------------------------- 1 | 'change_me' 5 | ]; -------------------------------------------------------------------------------- /Tests/Listener/KernelTerminateListenerTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\Listener; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use AppInsightsPHP\Client\Configuration; 18 | use AppInsightsPHP\Client\FailureCache; 19 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Listener\KernelTerminateListener; 20 | use ApplicationInsights\Channel\Telemetry_Channel; 21 | use ApplicationInsights\Telemetry_Client; 22 | use ApplicationInsights\Telemetry_Context; 23 | use PHPUnit\Framework\TestCase; 24 | use Psr\Log\NullLogger; 25 | use Psr\SimpleCache\CacheInterface; 26 | 27 | final class KernelTerminateListenerTest extends TestCase 28 | { 29 | public function test_do_nothing_when_instrumentation_key_is_empty() : void 30 | { 31 | $telemetryClientMock = $this->createMock(Telemetry_Client::class); 32 | $telemetryClientMock->method('getChannel')->willReturn($telemetryChannelMock = $this->createMock(Telemetry_Channel::class)); 33 | $telemetryClientMock->method('getContext')->willReturn($telemetryContextMock = $this->createMock(Telemetry_Context::class)); 34 | $telemetryContextMock->method('getInstrumentationKey')->willReturn(''); 35 | $telemetryChannelMock->method('getQueue')->willReturn([]); 36 | 37 | $client = new Client( 38 | $telemetryClientMock, 39 | Configuration::createDefault(), 40 | new FailureCache($this->createMock(CacheInterface::class)), 41 | new NullLogger() 42 | ); 43 | 44 | $telemetryClientMock->expects($this->never()) 45 | ->method('flush'); 46 | 47 | $listener = new KernelTerminateListener($client); 48 | 49 | $listener->onTerminate(); 50 | } 51 | 52 | public function test_do_nothing_when_telemetry_queue_is_empty() : void 53 | { 54 | $telemetryClientMock = $this->createMock(Telemetry_Client::class); 55 | $telemetryClientMock->method('getChannel')->willReturn($telemetryChannelMock = $this->createMock(Telemetry_Channel::class)); 56 | $telemetryClientMock->method('getContext')->willReturn($telemetryContextMock = $this->createMock(Telemetry_Context::class)); 57 | $telemetryContextMock->method('getInstrumentationKey')->willReturn('instrumentation_key'); 58 | $telemetryChannelMock->method('getQueue')->willReturn([]); 59 | 60 | $client = new Client( 61 | $telemetryClientMock, 62 | Configuration::createDefault(), 63 | new FailureCache($this->createMock(CacheInterface::class)), 64 | new NullLogger() 65 | ); 66 | 67 | $telemetryClientMock->expects($this->never()) 68 | ->method('flush'); 69 | 70 | $listener = new KernelTerminateListener($client); 71 | 72 | $listener->onTerminate(); 73 | } 74 | 75 | public function test_successful_flush() : void 76 | { 77 | $telemetryClientMock = $this->createMock(Telemetry_Client::class); 78 | $telemetryClientMock->method('getChannel')->willReturn($telemetryChannelMock = $this->createMock(Telemetry_Channel::class)); 79 | $telemetryClientMock->method('getContext')->willReturn($telemetryContextMock = $this->createMock(Telemetry_Context::class)); 80 | $telemetryContextMock->method('getInstrumentationKey')->willReturn('instrumentation_key'); 81 | $telemetryChannelMock->method('getQueue')->willReturn(['some_log_entry']); 82 | 83 | $client = new Client( 84 | $telemetryClientMock, 85 | Configuration::createDefault(), 86 | new FailureCache($this->createMock(CacheInterface::class)), 87 | new NullLogger() 88 | ); 89 | 90 | $telemetryClientMock->expects($this->once()) 91 | ->method('flush'); 92 | 93 | $listener = new KernelTerminateListener($client); 94 | 95 | $listener->onTerminate(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Tests/Twig/TelemetryExtensionTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Tests\Twig; 15 | 16 | use AppInsightsPHP\Client\ClientFactory; 17 | use AppInsightsPHP\Client\Configuration; 18 | use AppInsightsPHP\Symfony\AppInsightsPHPBundle\Twig\TelemetryExtension; 19 | use PHPUnit\Framework\TestCase; 20 | use Psr\Log\NullLogger; 21 | use Psr\SimpleCache\CacheInterface; 22 | 23 | final class TelemetryExtensionTest extends TestCase 24 | { 25 | public function test_app_insights_php_function_without_user_id() : void 26 | { 27 | $client = (new ClientFactory('instrumentation_key', Configuration::createDefault(), $this->createMock(CacheInterface::class), new NullLogger()))->create(); 28 | $client->getContext()->getOperationContext()->setId('operation_id'); 29 | $twigExtension = new TelemetryExtension($client); 30 | 31 | $this->assertSame( 32 | <<<'TWIG' 33 | 46 | TWIG 47 | , 48 | $twigExtension->appInsightsPHP() 49 | ); 50 | } 51 | 52 | public function test_app_insights_php_function_with_user_id() : void 53 | { 54 | $client = (new ClientFactory('instrumentation_key', Configuration::createDefault(), $this->createMock(CacheInterface::class), new NullLogger()))->create(); 55 | $client->getContext()->getOperationContext()->setId('operation_id'); 56 | $twigExtension = new TelemetryExtension($client); 57 | 58 | $this->assertSame( 59 | <<<'TWIG' 60 | 74 | TWIG 75 | , 76 | $twigExtension->appInsightsPHP('norbert@orzechowicz.pl') 77 | ); 78 | } 79 | 80 | public function test_app_insights_php_function_with_disabled_tracking() : void 81 | { 82 | $config = Configuration::createDefault(); 83 | $config->disable(); 84 | 85 | $client = (new ClientFactory('instrumentation_key', $config, $this->createMock(CacheInterface::class), new NullLogger()))->create(); 86 | $client->getContext()->getOperationContext()->setId('operation_id'); 87 | $twigExtension = new TelemetryExtension($client); 88 | 89 | $this->assertSame( 90 | <<<'TWIG' 91 | 94 | TWIG 95 | , 96 | $twigExtension->appInsightsPHP('norbert@orzechowicz.pl') 97 | ); 98 | } 99 | 100 | public function test_app_insights_php_function_with_empty_instrumentation_key() : void 101 | { 102 | $config = Configuration::createDefault(); 103 | 104 | $client = (new ClientFactory('', $config, $this->createMock(CacheInterface::class), new NullLogger()))->create(); 105 | $client->getContext()->getOperationContext()->setId('operation_id'); 106 | $twigExtension = new TelemetryExtension($client); 107 | 108 | $this->assertSame( 109 | <<<'TWIG' 110 | 113 | TWIG 114 | , 115 | $twigExtension->appInsightsPHP('norbert@orzechowicz.pl') 116 | ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Twig/TelemetryExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AppInsightsPHP\Symfony\AppInsightsPHPBundle\Twig; 15 | 16 | use AppInsightsPHP\Client\Client; 17 | use Twig\Extension\AbstractExtension; 18 | use Twig\TwigFunction; 19 | 20 | final class TelemetryExtension extends AbstractExtension 21 | { 22 | private $client; 23 | 24 | public function __construct(Client $client) 25 | { 26 | $this->client = $client; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function getFunctions() : array 33 | { 34 | return [ 35 | new TwigFunction('app_insights_php', [$this, 'appInsightsPHP'], ['is_safe' => ['html']]), 36 | ]; 37 | } 38 | 39 | public function appInsightsPHP(?string $userId = null) : string 40 | { 41 | $script = "'; 76 | 77 | return $script; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-insights-php/app-insights-php-bundle", 3 | "description": "Microsoft App Insights Symfony bundle", 4 | "type": "symfony-bundle", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Norbert Orzechowicz", 9 | "email": "norbert@orzechowicz.pl" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": {"AppInsightsPHP\\Symfony\\AppInsightsPHPBundle\\": ""}, 14 | "exclude-from-classmap": [ 15 | "/Tests/" 16 | ] 17 | }, 18 | "suggest": { 19 | "app-insights-php/doctrine-dependency-logger": "Trace Doctrine DBAL queries as dependency metric type." 20 | }, 21 | "require": { 22 | "php": "~8.1 || ~8.2", 23 | "app-insights-php/monolog-handler": "^0.3", 24 | "app-insights-php/client": "^0.3", 25 | "symfony/framework-bundle": "~3.4||~4.4||~5.4||~6", 26 | "guzzlehttp/guzzle": "^7.4", 27 | "twig/twig": "^1.2|^2|^3" 28 | }, 29 | "require-dev": { 30 | "app-insights-php/doctrine-dependency-logger": "^0.3", 31 | "friendsofphp/php-cs-fixer": "^3.4", 32 | "symfony/monolog-bundle": "~2||~3", 33 | "phpunit/phpunit": "^10" 34 | }, 35 | "scripts": { 36 | "phpunit": [ 37 | "phpunit --colors=always" 38 | ], 39 | "static:analyze": [ 40 | "php-cs-fixer fix --dry-run" 41 | ], 42 | "test" : [ 43 | "@phpunit" 44 | ], 45 | "cs:php:fix": [ 46 | "php-cs-fixer fix" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ./Tests 20 | 21 | 22 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import (fetchTarball "https://github.com/nixos/nixpkgs/archive/d44916d12f1d39baa02325040b381311364ad93a.tar.gz" ) {}; 3 | in 4 | 5 | let 6 | php = pkgs.php74.buildEnv { 7 | extensions = ({ enabled, all }: enabled++ [ all.pcov ]); 8 | extraConfig = 9 | '' 10 | short_open_tag = Off 11 | memory_limit = -1 12 | date.timezone = UTC 13 | ''; 14 | }; 15 | in 16 | 17 | pkgs.mkShell { 18 | name = "app-insights-php"; 19 | nativeBuildInputs = with pkgs; [ 20 | php 21 | php.packages.composer 22 | ]; 23 | } 24 | --------------------------------------------------------------------------------