I'm using Guzzle as a http client in a Symfony app. With the following code I'm able to log all requests and responses. Is there a way to log also response time?
I tried on_stats but it should be added to each client call and it is not an option.
services:
app.client.default:
class: 'GuzzleHttp\Client'
lazy: true
arguments:
- handler: '#app.handler_stack.default'
app.handler_stack.default:
class: 'GuzzleHttp\HandlerStack'
factory: [ GuzzleHttp\HandlerStack, create ]
calls:
- [ push, [ '#app.middleware.log_default', 'log_default' ] ]
app.middleware.log_default:
class: callback
factory: [ GuzzleHttp\Middleware, log ]
arguments:
- '#logger'
- '#app.log_formatter.default'
app.log_formatter.default:
class: 'GuzzleHttp\MessageFormatter'
arguments:
- ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"
app.service:
class: 'App\Service'
arguments:
- '#app.client.default'
use GuzzleHttp\Client;
class Service {
private $httpClient;
public function __construct(Client $httpClient) {
$this->httpClient = $httpClient;
}
public function apiRequest() {
$response = $this->httpClient->get('https://www.google.com');
// ...
}
}
Related
I hope someone could help me to use Api-platorm with Nelmio.
I use Api-plaform and Nelmio. I need to hide the Api-platform docs from Nelmio.
I need to have 3 routes:
/internal -> API-Platform Docs
/external -> NELMIO-Docs
/admin -> NELMIO-Docs
My config of Nelmio:
# config/packages/nelmio_api_doc.yaml
nelmio_api_doc:
documentation:
info:
title: ...
description: ...
version: 0.2.0
areas: # to filter documented areas
default:
path_patterns: [ ^/external ]
external:
path_patterns: [ ^/external ]
admin:
path_patterns: [ ^/admin ]
My config of Nelmio (routes):
# config/routes/nelmio_api_doc.yaml
app.swagger:
path: /{area}/json
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger, area: default }
app.swagger_ui:
path: /{area}
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger_ui, area: default }
My config of API-Platform:
# config/routes/api_platform.yaml
api_platform:
resource: .
type: api_platform
prefix: /internal/
But if I go to http://localhost/external or http://localhost/admin I see always not only needed routes, but also the routes from API-Platform:
I know this question is old by now, but I am facing the same situation and I found a workaround that may help some one, so I am posting it.
API Platform lets you decorate Swagger so you can customize the final documentation output. I took advantage of this to get rid of the entire api platform documentation when not asking for it.
<?php
namespace App\Swagger;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
final class SwaggerDecorator implements NormalizerInterface
{
private $decorated;
private $requestStack;
public function __construct(NormalizerInterface $decorated, RequestStack $requestStack)
{
$this->decorated = $decorated;
$this->requestStack = $requestStack;
}
public function normalize($object, $format = null, array $context = [])
{
if ('/internal/docs' !== $this->requestStack->getCurrentRequest()->getPathInfo()) {
// request is not for internal docs (maybe it is for external or admin one) so get rid of api platform docs
return null;
}
$docs = $this->decorated->normalize($object, $format, $context);
// here you can customize documentation
return $docs;
}
public function supportsNormalization($data, $format = null)
{
return $this->decorated->supportsNormalization($data, $format);
}
}
I hope this helps someone, happy coding!
UPDATE
In order to enable that decorator, you must declare it as so in your services file:
App\Swagger\SwaggerDecorator:
decorates: 'api_platform.swagger.normalizer.api_gateway'
arguments: [ '#App\Swagger\SwaggerDecorator.inner' ]
autoconfigure: false
Then, in the class itself, replace '/internal/docs' with the actual URL you are using for your API Platform documentation.
Hope this helps.
On your nelmio configuration yaml file, use a regex to exclude the docs. For instance, for excluding the /external/doc you should:
external:
path_patterns: [ ^/external(?!/doc$) ]
I have been using the Group annotation for years on SF2 and SF3.
I'm trying SF4.1. And I'm getting an empty JSON when I send a GET to my endpoint.
The interesting parts of my composer.json:
"friendsofsymfony/rest-bundle": "^2.3",
"jms/serializer-bundle": "^2.3",
"sensio/framework-extra-bundle": "^5.1",
"symfony/serializer-pack": "^1.0"
The config:
framework:
serializer:
enabled: true
enable_annotations: true
sensio_framework_extra:
view: { annotations: true }
fos_rest:
routing_loader:
default_format: json
view:
view_response_listener: 'force'
format_listener:
rules:
- { path: ^/, prefer_extension: true, fallback_format: json, priorities: [ json,xml, html ] }
The Entity
use JMS\Serializer\Annotation\Groups;
class User implements UserInterface, \Serializable
{
private $id;
/**
* #Groups({"api"})
*/
private $username;
And the endpoint API Controller:
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Context\Context;
use FOS\RestBundle\View\View;
class UserController extends FOSRestController {
public function getUserAction(Request $request, EntityManagerInterface $em)
{
$user = $em->getReference('App:User', 1);
$view = View::create();
$context = new Context();
$context->setGroups(['api']);
$view->setContext($context);
$view->setData($user);
return $this->handleView($view);
}
}
If I remove `$context->setGroups(['api']), the JSON has all the User attributes.
Any idea? Thanks!
Debug Info:
bin/console debug:container jms
Select one of the following services to display its information [fos_rest.serializer.jms]:
[0] fos_rest.serializer.jms
0
Information for Service "fos_rest.serializer.jms"
=================================================
Option Value
Service ID fos_rest.serializer.jms
Class FOS\RestBundle\Serializer\JMSSerializerAdapter
Tags -
Public no
Synthetic no
Lazy yes
Shared yes
Abstract no
Autowired no
Autoconfigured no
By default FOSRest prefers the JMSSerializer if it is installed. So first check if the service defined by the JMSSerializerBundle is defined:
./bin/console debug:container jms_serializer.serializer
If this command displays an error message (ServiceNotFound) then the bundle is not correctly installed. Check the config/bundles.php and add the following line if it's missing:
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
If it actually is installed, you can check the fos_rest configuration, if it maybe changes the serializer service. You can configure it like that:
fos_rest:
service:
serializer: "fos_rest.serializer.jms"
This must be a simple oversight - why isn't my entity_menus parameter being injected into the StoreController?
This is the error I am seeing:
Catchable Fatal Error: Argument 1 passed to AppBundle\Controller\Api\Admin\Common\StoreController::__construct() must be of the type array, none given
The relevant parts of services.yml
parameters:
entity_menus: [ 'manufacturers', 'vendors' ]
services:
app.admin.store_controller:
class: AppBundle\Controller\Api\Admin\Common\StoreController
arguments:
- '%entity_menus%'
The controller:
class StoreController extends FOSRestController
{
private $entityMenus;
public function __construct( Array $entityMenus )
{
$this->entityMenus = $entityMenus;
}
I updated services.yml like so to set the service container:
app.admin.store_controller:
class: AppBundle\Controller\Api\Admin\Common\StoreController
arguments: ['%entity_menus%']
calls:
- [setContainer, ["#service_container"]]
Thanks to: https://stackoverflow.com/a/19283476/2182349
I updated routing_rest.yml to use the service name and the class:
app_admin_common_store_menu:
type: rest
class: AppBundle\Controller\Api\Admin\Common\StoreController
resource: app.admin.store_controller
name_prefix: app_admin_api_store_
prefix: /api/store
Thanks to: https://github.com/FriendsOfSymfony/FOSRestBundle/issues/990
I made a command that automatically writes in routing.yml.
My problem is that when I try to browse one of the routes
api:
resource: "."
type: "api"
prefix: /api
I get this exception
Cannot load resource "."
I tried to add a cache:clear to my command but I get the same exception.
I added a cache warmup that runs after the command termination that way Symfony dumps routes into generated code .
class TerminateListener {
public function onConsoleTerminate(ConsoleTerminateEvent $event) {
if ($event->getCommand()->getName() == ('my:command')) {
$app = new Application();
$cache_clear_command = $event->getCommand()->getApplication()->find('cache:warmup');
$cache_clear_command->setApplication($app);
$event->getOutput()->setVerbosity('VERBOSITY_QUIET');
$cache_clear_command->run($event->getInput(), $event->getOutput());
}
}
}
services:
warmup.listener:
class:TerminateListener
tags:
- { name: kernel.event_listener, event: console.terminate , method: onConsoleTerminate }
I am working on a Symfony 2 web app and I would like to inject a Monolog logger using a specific channel to a service:
The Config:
monolog:
handlers:
main:
type: stream
path: %kernel.root_dir%/%kernel.environment%.log
level: error
#channels: [!alert]
alert:
type: stream
path: %kernel.root_dir%/%kernel.environment%.alert.log
level: info
channels: [alert]
Service Config:
services:
some_service:
class: Some\Service
arguments: [#logger]
tags:
- { name: monolog.logger, channel: alert }
The Service:
class SomeService {
protected $logger;
public function __construct($logger) {
$this->logger = $logger;
$this->logger->info('Log this!');
}
The prod.log file:
[2016-03-28 11:25:47] alert.INFO: Log this!
The Problem: Although I specifically inject the logger using the alert channel, the message is handled by the main handler. Thus the messages are logged into the prod.log file instead of the prod.alert.log file.
When I leave the line channels: [!alert] as comment, the message is logged to prod.log. When I activate this line by removing the comment, the message is not logged at all (main handler ignores the channel correctly).
What have I to to, in order to use a specific handler to target a specific log file, mailer, etc? Messages to alert channel should be handled by the alert handler while all other handlers are ignored.
Use special service created for Monolog handler:
services:
some_service:
class: Namespace\Some\Service
arguments: ["#monolog.logger.alert"]