Skip to content

Commit 3686b0a

Browse files
committed
Add ability to log API communication (debug log, info log, request / response file dump)
- Add ability to clear log files (Scheduled in Laravel) - No framework - no logging - Laravel - info logging
1 parent 33af9b7 commit 3686b0a

36 files changed

+1551
-194
lines changed

src/AbstractApi.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ abstract class AbstractApi implements ApiContract
3737
private readonly array $overrideEndpoints;
3838

3939
/**
40-
* @param AbstractEnvironment $environment
41-
* @param ApiFactoryContract $factory
4240
* @param array<class-string<AbstractEndpoint>, class-string<AbstractEndpoint>> $overrideEndpoints
4341
*/
4442
public function __construct(
@@ -91,7 +89,13 @@ public function get(
9189
->createRequest('GET', $uri->toString());
9290

9391
return $this->sendRequestAction
94-
->execute($this, $request, $responseClass, null, $headers, $expectedResponseStatusCode);
92+
->execute(
93+
api: $this,
94+
request: $request,
95+
responseClass: $responseClass,
96+
headers: $headers,
97+
expectedResponseStatusCode: $expectedResponseStatusCode
98+
);
9599
}
96100

97101
/**
@@ -116,7 +120,14 @@ public function post(
116120
->createRequest('POST', $uri->toString());
117121

118122
return $this->sendRequestAction
119-
->execute($this, $request, $responseClass, $body, $headers, $expectedResponseStatusCode);
123+
->execute(
124+
api: $this,
125+
request: $request,
126+
responseClass: $responseClass,
127+
body: $body,
128+
headers: $headers,
129+
expectedResponseStatusCode: $expectedResponseStatusCode
130+
);
120131
}
121132

122133
/**
@@ -141,7 +152,14 @@ public function put(
141152
->createRequest('PUT', $uri->toString());
142153

143154
return $this->sendRequestAction
144-
->execute($this, $request, $responseClass, $body, $headers, $expectedResponseStatusCode);
155+
->execute(
156+
api: $this,
157+
request: $request,
158+
responseClass: $responseClass,
159+
body: $body,
160+
headers: $headers,
161+
expectedResponseStatusCode: $expectedResponseStatusCode
162+
);
145163
}
146164

147165
/**

src/Actions/GetLoggerAction.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WrkFlow\ApiSdkBuilder\Actions;
6+
7+
use WrkFlow\ApiSdkBuilder\Contracts\SDKContainerFactoryContract;
8+
use WrkFlow\ApiSdkBuilder\Log\Contracts\LoggerContract;
9+
use WrkFlow\ApiSdkBuilder\Log\Entities\LoggerConfigEntity;
10+
use WrkFlow\ApiSdkBuilder\Log\Loggers\StackLogger;
11+
12+
/**
13+
* This action is responsible for getting the logger from the logger config.
14+
* - We expect, that container should make Loggers singletons (for performance reasons while mixing multiple APIs).
15+
* - We expect that cache holds in AbstractApi lifetime (SendRequestAction is created in API constructor
16+
* GetLoggerAction is its own dependency - cache will live between requests).
17+
*/
18+
class GetLoggerAction
19+
{
20+
private array $cache = [];
21+
22+
public function __construct(
23+
private readonly SDKContainerFactoryContract $container,
24+
) {
25+
}
26+
27+
public function execute(LoggerConfigEntity $config, string $host): ?LoggerContract
28+
{
29+
$loggerKey = $config->loggerByHost[$host] ?? $config->logger;
30+
31+
if (array_key_exists($loggerKey, $config->loggersMap->loggers) === false) {
32+
return null;
33+
}
34+
35+
if (array_key_exists($loggerKey, $this->cache)) {
36+
return $this->cache[$loggerKey];
37+
}
38+
39+
$logger = $this->getLogger(config: $config, loggerKey: $loggerKey);
40+
41+
$this->cache[$loggerKey] = $logger;
42+
43+
return $logger;
44+
}
45+
46+
protected function getLogger(LoggerConfigEntity $config, mixed $loggerKey): mixed
47+
{
48+
$loggers = $config->loggersMap->loggers[$loggerKey];
49+
50+
if ($loggers === []) {
51+
return null;
52+
}
53+
54+
if (count($loggers) === 1) {
55+
return $this->container->make($loggers[0]);
56+
}
57+
58+
return new StackLogger(
59+
loggers: array_map(callback: fn (string $logger) => $this->container->make($logger), array: $loggers)
60+
);
61+
}
62+
}

src/Actions/MakeApiFactory.php

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Closure;
88
use Http\Discovery\Psr17FactoryDiscovery;
99
use Http\Discovery\Psr18ClientDiscovery;
10-
use Psr\Container\ContainerInterface;
1110
use Psr\EventDispatcher\EventDispatcherInterface;
1211
use Psr\Http\Client\ClientInterface;
1312
use Psr\Http\Message\RequestFactoryInterface;
@@ -16,47 +15,48 @@
1615
use WrkFlow\ApiSdkBuilder\Contracts\ApiFactoryContract;
1716
use WrkFlow\ApiSdkBuilder\Contracts\SDKContainerFactoryContract;
1817
use WrkFlow\ApiSdkBuilder\Factories\ApiFactory;
18+
use WrkFlow\ApiSdkBuilder\Log\Entities\LoggerConfigEntity;
1919

2020
class MakeApiFactory
2121
{
2222
public function __construct(
23-
private readonly ContainerInterface $container,
24-
private readonly SDKContainerFactoryContract $factoryContract,
23+
private readonly SDKContainerFactoryContract $container,
2524
) {
2625
}
2726

2827
/**
2928
* Builds API factory using http discovery package
3029
*/
31-
public function execute(): ApiFactoryContract
30+
public function execute(LoggerConfigEntity $loggerConfig = null): ApiFactoryContract
3231
{
3332
$request = $this->containerOr(
34-
RequestFactoryInterface::class,
35-
fn () => Psr17FactoryDiscovery::findRequestFactory()
33+
interface: RequestFactoryInterface::class,
34+
create: static fn () => Psr17FactoryDiscovery::findRequestFactory()
3635
);
3736

38-
$client = $this->containerOr(ClientInterface::class, fn () => Psr18ClientDiscovery::find());
37+
$client = $this->containerOr(ClientInterface::class, static fn () => Psr18ClientDiscovery::find());
3938
$response = $this->containerOr(
40-
ResponseFactoryInterface::class,
41-
fn () => Psr17FactoryDiscovery::findResponseFactory()
39+
interface: ResponseFactoryInterface::class,
40+
create: static fn () => Psr17FactoryDiscovery::findResponseFactory()
4241
);
4342

4443
$stream = $this->containerOr(
45-
StreamFactoryInterface::class,
46-
fn () => Psr17FactoryDiscovery::findStreamFactory()
44+
interface: StreamFactoryInterface::class,
45+
create: static fn () => Psr17FactoryDiscovery::findStreamFactory()
4746
);
4847

4948
$eventDispatcher = $this->container->has(EventDispatcherInterface::class)
50-
? $this->container->get(EventDispatcherInterface::class)
49+
? $this->container->make(EventDispatcherInterface::class)
5150
: null;
5251

5352
return new ApiFactory(
5453
request: $request,
5554
client: $client,
5655
stream: $stream,
57-
container: $this->factoryContract,
56+
container: $this->container,
5857
response: $response,
59-
eventDispatcher: $eventDispatcher
58+
eventDispatcher: $eventDispatcher,
59+
loggerConfig: $loggerConfig
6060
);
6161
}
6262

@@ -71,7 +71,7 @@ public function execute(): ApiFactoryContract
7171
protected function containerOr(string $interface, Closure $create): mixed
7272
{
7373
if ($this->container->has($interface)) {
74-
return $this->container->get($interface);
74+
return $this->container->make($interface);
7575
}
7676

7777
return $create();

src/Actions/SendRequestAction.php

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
use WrkFlow\ApiSdkBuilder\Events\ResponseReceivedEvent;
2020
use WrkFlow\ApiSdkBuilder\Events\SendingRequestEvent;
2121
use WrkFlow\ApiSdkBuilder\Exceptions\ResponseException;
22+
use WrkFlow\ApiSdkBuilder\Log\Contracts\LoggerContract;
23+
use WrkFlow\ApiSdkBuilder\Log\Entities\LoggerConfigEntity;
2224
use WrkFlow\ApiSdkBuilder\Responses\AbstractResponse;
2325

24-
class SendRequestAction
26+
final class SendRequestAction
2527
{
2628
public function __construct(
2729
private readonly BuildHeadersAction $buildHeadersAction,
2830
private readonly MakeBodyFromResponseAction $makeBodyFromResponseAction,
31+
private readonly GetLoggerAction $getLoggerAction,
2932
) {
3033
}
3134

@@ -57,6 +60,11 @@ public function execute(
5760
->factory()
5861
->eventDispatcher();
5962

63+
$loggerConfig = $api->factory()
64+
->loggerConfig();
65+
66+
$logger = $this->getLoggerAction->execute(config: $loggerConfig, host: $request->getUri()->getHost());
67+
6068
$environment = $api->environment();
6169

6270
$request = $this->buildRequest($environment, $api, $headers, $request, $body);
@@ -65,6 +73,8 @@ public function execute(
6573
environment: $environment,
6674
api: $api,
6775
dispatcher: $dispatcher,
76+
logger: $logger,
77+
loggerConfig: $loggerConfig,
6878
request: $request,
6979
responseClass: $responseClass,
7080
requestId: $id,
@@ -78,6 +88,8 @@ public function execute(
7888
expectedResponseStatusCode: $expectedResponseStatusCode,
7989
responseClass: $responseClass,
8090
dispatcher: $dispatcher,
91+
logger: $logger,
92+
loggerConfig: $loggerConfig,
8193
id: $id,
8294
request: $request,
8395
timeStart: $timeStart
@@ -87,10 +99,12 @@ public function execute(
8799
/**
88100
* @param class-string<AbstractResponse> $responseClass
89101
*/
90-
protected function sendRequest(
102+
private function sendRequest(
91103
AbstractEnvironment $environment,
92104
AbstractApi $api,
93105
?EventDispatcherInterface $dispatcher,
106+
?LoggerContract $logger,
107+
LoggerConfigEntity $loggerConfig,
94108
RequestInterface $request,
95109
string $responseClass,
96110
string $requestId,
@@ -100,11 +114,11 @@ protected function sendRequest(
100114
try {
101115
$dispatcher?->dispatch(new SendingRequestEvent($requestId, $request, $timeStart));
102116

103-
if ($fakedResponse !== null) {
117+
if ($fakedResponse instanceof ResponseInterface) {
104118
return $fakedResponse;
105119
} elseif ($environment instanceof EnvironmentFakeResponseContract) {
106120
$response = $environment->getResponse($request, $responseClass, $api->factory());
107-
if ($response !== null) {
121+
if ($response instanceof ResponseInterface) {
108122
return $response;
109123
}
110124
}
@@ -113,18 +127,20 @@ protected function sendRequest(
113127
->client()
114128
->sendRequest($request);
115129
} catch (Exception $exception) {
116-
$dispatcher?->dispatch(new RequestConnectionFailedEvent(
130+
$event = new RequestConnectionFailedEvent(
117131
id: $requestId,
118132
request: $request,
119133
exception: $exception,
120134
requestDurationInSeconds: $this->getRequestDuration($timeStart)
121-
));
135+
);
136+
$dispatcher?->dispatch($event);
137+
$logger?->requestConnectionFailed(event: $event, config: $loggerConfig);
122138

123139
throw $exception;
124140
}
125141
}
126142

127-
protected function withBody(
143+
private function withBody(
128144
AbstractApi $api,
129145
OptionsContract|StreamInterface|string|null $body,
130146
RequestInterface $request
@@ -142,7 +158,7 @@ protected function withBody(
142158
return $request;
143159
}
144160

145-
protected function getRequestDuration(float $timeStart): float
161+
private function getRequestDuration(float $timeStart): float
146162
{
147163
return (float) microtime(true) - $timeStart;
148164
}
@@ -157,12 +173,14 @@ protected function getRequestDuration(float $timeStart): float
157173
*
158174
* @return TResponse
159175
*/
160-
protected function handleResponse(
176+
private function handleResponse(
161177
AbstractApi $api,
162178
ResponseInterface $response,
163179
?int $expectedResponseStatusCode,
164180
string $responseClass,
165181
?EventDispatcherInterface $dispatcher,
182+
?LoggerContract $logger,
183+
LoggerConfigEntity $loggerConfig,
166184
string $id,
167185
RequestInterface $request,
168186
float $timeStart
@@ -179,12 +197,14 @@ protected function handleResponse(
179197

180198
$finalResponse = $container->makeResponse($responseClass, $response, $body);
181199

182-
$dispatcher?->dispatch(new ResponseReceivedEvent(
200+
$event = new ResponseReceivedEvent(
183201
id: $id,
184202
request: $request,
185203
response: $finalResponse,
186204
requestDurationInSeconds: $this->getRequestDuration($timeStart)
187-
));
205+
);
206+
$dispatcher?->dispatch($event);
207+
$logger?->responseReceivedEvent(event: $event, config: $loggerConfig);
188208

189209
return $finalResponse;
190210
}
@@ -196,18 +216,20 @@ protected function handleResponse(
196216
$exception = new ResponseException($response, $exception->getMessage(), $exception);
197217
}
198218

199-
$dispatcher?->dispatch(new RequestFailedEvent(
219+
$event = new RequestFailedEvent(
200220
id: $id,
201221
request: $request,
202222
exception: $exception,
203223
requestDurationInSeconds: $this->getRequestDuration($timeStart)
204-
));
224+
);
225+
$dispatcher?->dispatch($event);
226+
$logger?->requestFailed(event: $event, config: $loggerConfig);
205227

206228
throw $exception;
207229
}
208230
}
209231

210-
protected function buildRequest(
232+
private function buildRequest(
211233
AbstractEnvironment $environment,
212234
AbstractApi $api,
213235
array $headers,

src/Contracts/ApiFactoryContract.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Psr\Http\Message\RequestFactoryInterface;
1010
use Psr\Http\Message\ResponseFactoryInterface;
1111
use Psr\Http\Message\StreamFactoryInterface;
12+
use WrkFlow\ApiSdkBuilder\Log\Entities\LoggerConfigEntity;
1213

1314
interface ApiFactoryContract
1415
{
@@ -23,4 +24,6 @@ public function response(): ResponseFactoryInterface;
2324
public function container(): SDKContainerFactoryContract;
2425

2526
public function eventDispatcher(): ?EventDispatcherInterface;
27+
28+
public function loggerConfig(): LoggerConfigEntity;
2629
}

0 commit comments

Comments
 (0)