Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/injectable transport #116

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

Lewiscowles1986
Copy link
Contributor

@Lewiscowles1986 Lewiscowles1986 commented Jan 29, 2022

Fixes #115

  • Proposed code changes (minimal, interface based)
  • Test fix (I was authoring on Windows, and it was a pain, I could not run tests. It's 2-liner)
  • GitHub actions (until I'd fixed, I actually had a really slow feedback loop)
  • Editor config and git attributes
  • Proposed Documentation (words are hard, forgive me if it's not perfect).
  • Update to support PHP 8.0 & 8.1 thanks to Yoast
  • Tests
  • PHP 5.6 - PHP 8.1 (circleci missing 8.1-node-browsers image 🤷 )
  • Linting, Code-Style

The actual guard portion of this, doesn't have to be owned by this repo. In the README.md I added a section with example to bypass the magic, re-using the Ipv4Udp transport that is part of this PR, so that an example of instrumenting the control I was looking for (bypassing the magic in current constructor) is available.

I'm not 💯 sure about the construct with single method interface, but felt that opening and closing the socket separately to the functionality; could make some future testing easier. I Also didn't want to open a long-lived socket (although the classes don't hint at that, the way they are used in one place enforces for-now).

The interfaces are pretty stripped-back to deal with PHP 5.6 minimum. Not sure where DataDog is going with that, but I think users would get a lot of benefit rolling up to only 7.4 & 8+ in a future release.

Why interfaces

Short answer. I Hate the array approach. It's too open a design, and leads to choices where spooky [mis]configuration can happen. While this one interface won't change the entire codebase, it nudges in a direction a lot of PHP coders are going.

I'm not sure that adding strict_mode to counteract the magic will make this code any more readable or be the right way to counteract the magic cascade. For an upstream vendor to us this, implement the interface, I believe is a far more intentional choice, that leaves the majority of users happy where they are now. Internal getenv may be a feature for people who have not had silent misconfiguration lead to confusing bugs.

Another reason for an interface based approach is that I think a test implementation that allows spy test-double would be a nice thing that this allows. Instead of using curl spy test class and the socket spy test class, which are namespace coordination dependent.

Lastly, This interface approach would enable any coder:

  • To perform unit or integration level tests without needing to setup DataDog agent at all.
  • Could also help if falling-back in cases where perhaps the transport might use a PSR Logger, and send using Datadog API key as a log message.

@gh123man
Copy link
Member

I like that this is a non-invasive change and should not break existing users.
Also thank you for the work in including PHP 8 support.

Could you add a more concrete of an example (or describe) as to why this approach is advantageous over checking the host/port configuration prior to constructing DogStatsd? Is there another transport type other than the UDP/UDS you are looking to support?

.github/workflows/ci.yml Outdated Show resolved Hide resolved
src/TransportFactory.php Show resolved Hide resolved
@Lewiscowles1986 Lewiscowles1986 force-pushed the feature/injectable-transport branch from d992eb3 to f9f4569 Compare February 2, 2022 03:08
Minor: fopen('/dev/null', 'r') is not cross-platform.

As much as this will run in a *nix environment.
I like to run test-suites on ANY reasonable machine.
Thanks to Yoast!
@Lewiscowles1986 Lewiscowles1986 force-pushed the feature/injectable-transport branch from f9f4569 to 2a1c935 Compare February 2, 2022 03:22
Comment on lines -468 to -476
$socket = is_null($this->socketPath) ? socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)
: socket_create(AF_UNIX, SOCK_DGRAM, 0);
socket_set_nonblock($socket);

if (!is_null($this->socketPath)) {
$res = socket_sendto($socket, $message, strlen($message), 0, $this->socketPath);
} else {
$res = socket_sendto($socket, $message, strlen($message), 0, $this->host, $this->port);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, this is a win.

Comment on lines +99 to +104
$this->transportFactory = (
isset($config['transport_factory']) ?
$transportFactory :
new BridgingTransportFactory($this->socketPath, $this->host, $this->port)
);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I had hoped might be pointed out is that this could be an alternative to https://github.com/DataDog/php-datadogstatsd/pull/116/files#diff-baf3cb376e36dfcf767f6389163de7d731798b1b108a44f33a2047e8e98bae3bR88-R98

I didn't want to leap too far in a single refactoring; and I think this should be part of a Configuration, that specific factory implementations could offer. So that implementations of transport are simple, and do not have cascading logic, or magic coming from the environment; but another layer can add that for them, or omit that, as the example in the README.md does.

Copy link
Member

@gh123man gh123man left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are pretty close. Nice PR - and nice additions.
Mostly comments about the documentation and wording.

@@ -465,15 +477,7 @@ public function flush($message)
$message .= $this->flushTelemetry();

// Non - Blocking UDP I/O - Use IP Addresses!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: This comment can be removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comment?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep - this comment is no longer relevant since the implementation moved (plus It's not a great comment to begin with)

@@ -46,6 +46,64 @@ $statsd = new DogStatsd(

DogStatsd constructor, takes a configuration array. See the full list of available [DogStatsD Client instantiation parameters](https://docs.datadoghq.com/developers/dogstatsd/?code-lang=php#client-instantiation-parameters).

#### Migrating from legacy configuration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call this section "Using a custom transport factory"

Overall I think this whole section is valuable but perhaps a bit verbose for the readme. Could you create a root level docs folder with this as a markdown document and link to it from the readme?

In this readme you could have a brief section

If you want more control over how transports are constructed see custom-transport-factory.md on how to implement a custom transport factory.

}
```

##### Instantiating datadog with your new class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
##### Instantiating datadog with your new class
##### Instantiating DogStatsd with your custom transport factory

namespace YourOrg\Metrics;

/**
* This class works around the decisions the constructor of DogStatsd class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: reword to something like:

Custom transport factory that validates a host and port are not empty. This extends the default behavior where these errors would otherwise fail silently.

@@ -46,6 +46,64 @@ $statsd = new DogStatsd(

DogStatsd constructor, takes a configuration array. See the full list of available [DogStatsD Client instantiation parameters](https://docs.datadoghq.com/developers/dogstatsd/?code-lang=php#client-instantiation-parameters).

#### Migrating from legacy configuration

In some setups, the use of environment variables can cause surprises, such as warnings, test-failures, etc.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: just want to reword this a bit. We prefer to be brief in the readme with a focus on usage. If you feel that the extended explanation is valuable, please add it in the code comments or in a dedicated document (as suggested above)

Perhaps something like (replacing lines 51-53):

Using environment variables for configuration may not always be ideal. If you want extra control over how transports are constructed or validated consider implementing a custom TransportFactory. You can achieve this by implanting the DataDog\TransportFactoryinterface and suppling it to thetransport_factory` configuration.

);
```

#### Why this structure?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can drop this section from the readme. Perhaps add this as a code comment (or feel free to include it in a separate document)

@@ -19,16 +19,28 @@ x-dockerbuild-5: &dockerbuild-5
- run: composer lint

jobs:
test-8.0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the extended test coverage 💯

@gh123man
Copy link
Member

Ive cherrypicked the test fixes from this PR #127 and merged them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Constructor guards
2 participants