From 2e1127aacb0a5b1f93cb4ccef41aae1e104b4b92 Mon Sep 17 00:00:00 2001 From: Lyal Avery Date: Thu, 21 Dec 2017 18:42:24 -0600 Subject: [PATCH] First version of monolog-logdna --- CHANGELOG.md | 22 +++++++++++ CODE_OF_CONDUCT.md | 74 +++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 32 ++++++++++++++++ ISSUE_TEMPLATE.md | 27 ++++++++++++++ LICENSE.md | 21 +++++++++++ PULL_REQUEST_TEMPLATE.md | 43 +++++++++++++++++++++ README.md | 75 +++++++++++++++++++++++++++++++++++++ composer.json | 57 ++++++++++++++++++++++++++++ phpunit.xml.dist | 32 ++++++++++++++++ src/LogDNAFormatter.php | 52 ++++++++++++++++++++++++++ src/LogDNAHandler.php | 80 ++++++++++++++++++++++++++++++++++++++++ tests/FormatterTest.php | 57 ++++++++++++++++++++++++++++ tests/HandlerTest.php | 41 ++++++++++++++++++++ 13 files changed, 613 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 ISSUE_TEMPLATE.md create mode 100644 LICENSE.md create mode 100644 PULL_REQUEST_TEMPLATE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 phpunit.xml.dist create mode 100644 src/LogDNAFormatter.php create mode 100644 src/LogDNAHandler.php create mode 100644 tests/FormatterTest.php create mode 100644 tests/HandlerTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..853e9c9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to `monolog-logdna` will be documented in this file. + +Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. + +## NEXT - YYYY-MM-DD + +### Added +- Nothing + +### Deprecated +- Nothing + +### Fixed +- Nothing + +### Removed +- Nothing + +### Security +- Nothing diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..92f2d0f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at `lyal@pullrequest.com`. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bfc9ce9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +We accept contributions via Pull Requests on [Github](https://github.com/lyal/monolog-logdna). + + +## Pull Requests + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - Check the code style with ``$ composer check-style`` and fix it with ``$ composer fix-style``. + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. + +- **Create feature branches** - Don't ask us to pull from your master branch. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + + +## Running Tests + +``` bash +$ composer test +``` + + +**Happy coding**! diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..d34bc07 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,27 @@ + + +## Detailed description + +Provide a detailed description of the change or addition you are proposing. + +Make it clear if the issue is a bug, an enhancement or just a question. + +## Context + +Why is this change important to you? How would you use it? + +How can it benefit other users? + +## Possible implementation + +Not obligatory, but suggest an idea for implementing addition or change. + +## Your environment + +Include as many relevant details about the environment you experienced the bug in and how to reproduce it. + +* Version used (e.g. PHP 7.0, PHP 7.1): +* Operating system and version (e.g. Ubuntu 16.04, Windows 7): +* Link to your project: +* ... +* ... diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..05cc24b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) 2017 Lyal Avery + +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..86246b3 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,43 @@ + + +## Description + +Describe your changes in detail. + +## Motivation and context + +Why is this change required? What problem does it solve? + +If it fixes an open issue, please link to the issue here (if you write `fixes #num` +or `closes #num`, the issue will be automatically closed when the pull is accepted.) + +## How has this been tested? + +Please describe in detail how you tested your changes. + +Include details of your testing environment, and the tests you ran to +see how your change affects other areas of the code, etc. + +## Screenshots (if appropriate) + +## Types of changes + +What types of changes does your code introduce? Put an `x` in all the boxes that apply: +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to change) + +## Checklist: + +Go over all the following points, and put an `x` in all the boxes that apply. + +Please, please, please, don't send your pull request until all of the boxes are ticked. Once your pull request is created, it will trigger a build on our [continuous integration](http://www.phptherightway.com/#continuous-integration) server to make sure your [tests and code style pass](https://help.github.com/articles/about-required-status-checks/). + +- [ ] I have read the **[CONTRIBUTING](CONTRIBUTING.md)** document. +- [ ] My pull request addresses exactly one patch/feature. +- [ ] I have created a branch for this patch/feature. +- [ ] Each individual commit in the pull request is meaningful. +- [ ] I have added tests to cover my changes. +- [ ] If my change requires a change to the documentation, I have updated it accordingly. + +If you're unsure about any of these, don't hesitate to ask. We're here to help! diff --git a/README.md b/README.md new file mode 100644 index 0000000..5739be5 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# monolog-logdna + +[![Latest Version on Packagist][ico-version]][link-packagist] +[![Software License][ico-license]](LICENSE.md) +[![Build Status][ico-travis]][link-travis] +[![Coverage Status][ico-scrutinizer]][link-scrutinizer] +[![Quality Score][ico-code-quality]][link-code-quality] +[![Total Downloads][ico-downloads]][link-downloads] + +## Install + +Via Composer + +``` bash +$ composer require lyal/monolog-logdna +``` + +## Usage + +``` php +$logger = new Logger('general'); +$logdnaHandler = new LogDNAHandler(); +$logger->debug('this is my message!'); +``` + +## Notes + +Unlike other monolog implementations of json-based log providers, this currently defaults to one request rather than retrying on failure; +this will result in a faster request lifecycle and will prevent accidental failure ddosing of LogDNA. + +## Environment Variables + +You can set two environment variables for this library: + +*LOGDNA_INGESTION_KEY* -- the ingestion key provided in your LogDNA key + if (getenv('LOGDNA_API_URL')) { + + +## Testing + +Testing is run through phpunit -- environment variables can be tweaked. + +``` bash +phpunit +``` + +## Contributing + +Please see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details. + +## Security + +If you discover any security related issues, please email lyal@pullrequest.com instead of using the issue tracker. + +## Credits + +- [Lyal Avery][link-author] + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. + +[ico-version]: https://img.shields.io/packagist/v/lyal/monolog-logdna.svg?style=flat-square +[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square +[ico-travis]: https://img.shields.io/travis/lyal/monolog-logdna/master.svg?style=flat-square +[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/lyal/monolog-logdna.svg?style=flat-square +[ico-code-quality]: https://img.shields.io/scrutinizer/g/lyal/monolog-logdna.svg?style=flat-square +[ico-downloads]: https://img.shields.io/packagist/dt/lyal/monolog-logdna.svg?style=flat-square + +[link-packagist]: https://packagist.org/packages/lyal/monolog-logdna +[link-travis]: https://travis-ci.org/lyal/monolog-logdna +[link-scrutinizer]: https://scrutinizer-ci.com/g/lyal/monolog-logdna/code-structure +[link-code-quality]: https://scrutinizer-ci.com/g/lyal/monolog-logdna +[link-downloads]: https://packagist.org/packages/lyal/monolog-logdna +[link-author]: https://github.com/lyal diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..ebcfe9f --- /dev/null +++ b/composer.json @@ -0,0 +1,57 @@ +{ + "name": "lyal/monolog-logdna", + "type": "library", + "description": "An MIT-licensed package to add LogDNA support to Monolog", + "keywords": [ + "logdna", + "monolog", + "monolog-logdna", + "laravel", + "psr-3", + "logging", + "log" + ], + "homepage": "https://github.com/lyal/monolog-logdna", + "license": "MIT", + "authors": [ + { + "name": "Lyal Avery", + "email": "lyal@pullrequest.com", + "homepage": "https://github.com/lyal", + "role": "Developer" + } + ], + "require": { + "php": "~7.0|~7.1", + "guzzlehttp/guzzle": "^6.3", + "monolog/monolog": "^1.23", + "nesbot/carbon": "^1.22" + }, + "require-dev": { + "phpunit/phpunit": ">=5.4.3", + "squizlabs/php_codesniffer": "^2.3" + }, + "autoload": { + "psr-4": { + "Lyal\\MonologLogDNA\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Lyal\\MonologLogDNA\\": "tests" + } + }, + "scripts": { + "test": "phpunit", + "check-style": "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests", + "fix-style": "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests" + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..b7c9879 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,32 @@ + + + + + tests + + + + + + + + src/ + + + + + + + + + + diff --git a/src/LogDNAFormatter.php b/src/LogDNAFormatter.php new file mode 100644 index 0000000..dd0ca7e --- /dev/null +++ b/src/LogDNAFormatter.php @@ -0,0 +1,52 @@ +carbon = new Carbon; + $this->environment = $environment ?? 'unknown'; + } + + public function format(array $record) :string + { + $payload = [ + 'lines' => [$this->formatLine($record)] + ]; + return parent::format($payload); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + $payload = ['lines' => []]; + + foreach ($records as $record) { + $payload['lines'][] = $this->formatLine($record); + } + + return json_encode($payload); + } + + private function formatLine(array $record) :array + { + return [ + 'timestamp' => $this->carbon->getTimestamp(), + 'line' => $record['message'], + 'app' => $record['channel'], + 'level' => $record['level_name'], + 'env' => $this->environment, + 'meta' => $record['context'] + ]; + } +} diff --git a/src/LogDNAHandler.php b/src/LogDNAHandler.php new file mode 100644 index 0000000..34142c8 --- /dev/null +++ b/src/LogDNAHandler.php @@ -0,0 +1,80 @@ +key = $key ?? getenv('LOGDNA_INGESTION_KEY'); + $this->carbon = new Carbon; + $this->hostname = $host ?? getenv('LOGDNA_HOSTNAME') ?: gethostname(); + $this->mac = $mac; + $this->ip = $ip ?? getenv('LOGDNA_HOST_IP') ?: gethostbyname(gethostname()); + + + $this->setHttpClient($httpClient ?? new Client()); + + if (getenv('LOGDNA_API_URL')) { + $this->api = getenv('LOGDNA_API_URL'); + } + } + + /** + * @param Client $client + */ + + protected function setHttpClient(Client $client) + { + $this->httpClient = $client; + } + + protected function write(array $record) + { + $response = $this->httpClient->post($this->api, [ + 'headers' => [ + 'Content-Type' => 'application/json', + + ], + 'query' => [ + 'hostname' => $this->hostname, + 'mac' => $this->mac, + 'ip' => $this->ip, + 'now' => $this->carbon->getTimestamp() + ], + 'auth' => [$this->key, ''], + 'body' => $record['formatted'] + ]); + } + + protected function getDefaultFormatter(): FormatterInterface + { + return new LogDNAFormatter(); + } +} diff --git a/tests/FormatterTest.php b/tests/FormatterTest.php new file mode 100644 index 0000000..e3e821e --- /dev/null +++ b/tests/FormatterTest.php @@ -0,0 +1,57 @@ +logDNAFormatter = new LogDNAFormatter(); + } + + public function testFormatResponse() + { + $record = [ + 'message' => 'testing', + 'channel' => 'general', + 'level_name' => 'DEBUG', + 'context' => [] + ]; + + $record = json_decode($this->logDNAFormatter->format($record))->lines{0}; + $this->assertEquals($record->env, 'unknown'); + $this->assertEquals($record->line, 'testing'); + $this->assertEquals($record->level, 'DEBUG'); + $this->assertEquals($record->meta, []); + $this->assertEquals($record->app, 'general'); + } + + public function testBatchFormat() + { + $record = [ + [ + 'message' => 'testing1', + 'channel' => 'general', + 'level_name' => 'DEBUG', + 'context' => [] + ], + [ + 'message' => 'testing2', + 'channel' => 'general', + 'level_name' => 'DEBUG', + 'context' => [] + ], + + ]; + + $records = json_decode($this->logDNAFormatter->formatBatch($record))->lines; + + $this->assertEquals($records[0]->line, 'testing1'); + $this->assertEquals($records[1]->line, 'testing2'); + } +} diff --git a/tests/HandlerTest.php b/tests/HandlerTest.php new file mode 100644 index 0000000..87d20f2 --- /dev/null +++ b/tests/HandlerTest.php @@ -0,0 +1,41 @@ +container); + $handler = HandlerStack::create($mock); + $handler->push($history); + $client = new Client(['handler' => $handler]); + $this->logDNAHandler = new LogDNAHandler($key = 'testingkey', 'test', 'test', '192.1.1.1', $client); + $this->logger = new Logger('general'); + $this->logger->pushHandler($this->logDNAHandler); + } + + public function testHandlerWrite() + { + $this->logger->debug('test'); + $this->assertEquals($this->container[0]['response']->getStatusCode(), 200); + } +}