Pracuję nad spoczynkowym API z wiosennym bagażnikiem. Muszę rejestrować wszystkie żądania za pomocą parametrów wejściowych (metodami, np. GET, POST itp.), Ścieżką żądania, ciągiem zapytania, odpowiednią metodą klasy tego żądania, a także odpowiedzią na to działanie, zarówno sukcesem, jak i błędami.
Dla przykładu:
udane żądanie:
http://example.com/api/users/1
Dziennik powinien wyglądać mniej więcej tak:
{
HttpStatus: 200,
path: "api/users/1",
method: "GET",
clientIp: "0.0.0.0",
accessToken: "XHGu6as5dajshdgau6i6asdjhgjhg",
method: "UsersController.getUser",
arguments: {
id: 1
},
response: {
user: {
id: 1,
username: "user123",
email: "[email protected]"
}
},
exceptions: []
}
Lub poproś o błąd:
http://example.com/api/users/9999
Dziennik powinien być mniej więcej taki:
{
HttpStatus: 404,
errorCode: 101,
path: "api/users/9999",
method: "GET",
clientIp: "0.0.0.0",
accessToken: "XHGu6as5dajshdgau6i6asdjhgjhg",
method: "UsersController.getUser",
arguments: {
id: 9999
},
returns: {
},
exceptions: [
{
exception: "UserNotFoundException",
message: "User with id 9999 not found",
exceptionId: "adhaskldjaso98d7324kjh989",
stacktrace: ...................
]
}
Chcę, aby żądanie / odpowiedź było pojedynczą jednostką z niestandardowymi informacjami związanymi z tą jednostką, zarówno w przypadkach udanych, jak i błędów.
Jaka jest najlepsza praktyka wiosną, aby to osiągnąć, może być z filtrami? jeśli tak, czy możesz podać konkretny przykład?
(Grałem z @ControllerAdvice i @ExceptionHandler, ale jak już wspomniałem, muszę obsłużyć wszystkie żądania dotyczące sukcesu i błędów w jednym miejscu (i jednym dzienniku)).
źródło
HandlerInterceptor
, ale to nie może pracować również z zalogowaniu odpowiedź jak wspomniano w odpowiedzi: concretepage.com/spring/spring-mvc/... - HandlerInterceptor ma dostęp do metody (metoda: „UsersController.getUser”). Nie jest to znane w filtrze serwletu.LogClass{ getRequestAndSaveIt()} Gson.toJson(LogClass)
jako pseudokodOdpowiedzi:
Nie pisz żadnych elementów przechwytujących, filtrów, komponentów, aspektów itp., Jest to bardzo częsty problem, który został rozwiązany wiele razy.
Spring Boot ma moduły o nazwie Actuator , które umożliwiają wylogowanie żądania HTTP po wyjęciu z pudełka. Istnieje punkt końcowy odwzorowany na
/trace
(SB1.x) lub/actuator/httptrace
(SB2.0 +), który pokaże Ci ostatnie 100 żądań HTTP. Możesz go dostosować do rejestrowania każdego żądania lub zapisywać w bazie danych.Aby uzyskać pożądane punkty końcowe, potrzebujesz zależności rozruchu rozruchowego i elementu wykonawczego, a także „białej listy” punktów końcowych, których szukasz, i ewentualnie skonfiguruj lub wyłącz zabezpieczenia.
Ponadto, gdzie będzie działać ta aplikacja? Czy będziesz korzystać z PaaS? Dostawcy usług hostingowych, na przykład Heroku, zapewniają rejestrowanie żądań w ramach swoich usług i nie trzeba wtedy żadnego kodowania.
źródło
Wiosna już zapewnia filtr, który wykonuje tę pracę. Dodaj następującą fasolę do swojej konfiguracji
Nie zapomnij zmienić poziomu dziennika
org.springframework.web.filter.CommonsRequestLoggingFilter
naDEBUG
.źródło
Możesz użyć
javax.servlet.Filter
jeśli nie ma wymogu rejestrowania metody Java, która została wykonana.Ale z tego wymogu masz dostępu do informacji przechowywanych w
handlerMapping
zDispatcherServlet
. To powiedziawszy, możesz zastąpićDispatcherServlet
aby wykonać rejestrowanie pary żądanie / odpowiedź.Poniżej znajduje się przykład pomysłu, który można ulepszyć i dostosować do własnych potrzeb.
HandlerExecutionChain
- zawiera informacje o module obsługi zapytań.Następnie możesz zarejestrować tego dyspozytora w następujący sposób:
A oto próbka dzienników:
AKTUALIZACJA
W przypadku błędów Spring wykonuje automatyczną obsługę błędów. Dlatego
BasicErrorController#error
jest wyświetlany jako moduł obsługi żądań. Jeśli chcesz zachować oryginalny moduł obsługi żądań, możesz zastąpić to zachowaniespring-webmvc-4.2.5.RELEASE-sources.jar!/org/springframework/web/servlet/DispatcherServlet.java:971
wcześniej#processDispatchResult
wywołanym, aby buforować oryginalny moduł obsługi.źródło
Logbook biblioteka jest wykonany specjalnie dla żądań HTTP rejestrowania i odpowiedzi. Obsługuje Spring Boot przy użyciu specjalnej biblioteki startowej.
Aby włączyć logowanie Spring Boot, wystarczy dodać bibliotekę do zależności projektu. Na przykład zakładając, że używasz Maven:
Domyślnie dane wyjściowe rejestrowania wyglądają następująco:
Nie wyświetla jednak nazwy klasy, która obsługuje żądanie. Biblioteka ma kilka interfejsów do pisania niestandardowych programów rejestrujących.
źródło
logging.level.org.zalando.logbook=TRACE
do swojegoapplication.properties
(jak stwierdzono wReadme
)Zdefiniowałem poziom logowania
application.properties
do drukowania żądań / odpowiedzi, adres URL metody w pliku dziennikaUżyłem Spring Boot.
źródło
Oto jak to zrobić w wiosennym spoczynku danych przy użyciu
org.springframework.web.util.ContentCachingRequestWrapper
iorg.springframework.web.util.ContentCachingResponseWrapper
źródło
Jeśli nie masz nic przeciwko wypróbowaniu Spring AOP, to jest coś, co badałem do celów logowania i działa całkiem dobrze dla mnie. Nie rejestruje jednak żądań, które nie zostały zdefiniowane, i nieudanych prób żądania.
Dodaj te trzy zależności
Dodaj to do pliku konfiguracyjnego xml
<aop:aspectj-autoproxy/>
Utwórz adnotację, która może być używana jako punkt orientacyjny
Teraz dodaj adnotacje do wszystkich pozostałych metod API, które chcesz zalogować
Przejdźmy do aspektu. skanuj komponent pakiet, w którym znajduje się ta klasa.
Jeśli chcesz przeczytać szczegółowo, przeczytaj to. http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
źródło
Po dodaniu siłowników do aplikacji opartej na rozruchu wiosennym masz
/trace
punkt końcowy dostępny z najnowszymi informacjami o żądaniach. Ten punkt końcowy działa w oparciu o TraceRepository, a domyślną implementacją jest InMemoryTraceRepository, która zapisuje 100 ostatnich połączeń. Możesz to zmienić, wdrażając ten interfejs samodzielnie i udostępniając go jako Spring Bean. Na przykład do rejestrowania wszystkich żądań do logowania (i nadal używam domyślnej implementacji jako podstawowej pamięci do udostępniania informacji na/trace
punkcie końcowym) Używam tego rodzaju implementacji:Ta
traceInfo
mapa zawiera podstawowe informacje na temat żądania i odpowiedzi na tego rodzaju postaci:{method=GET, path=/api/hello/John, headers={request={host=localhost:8080, user-agent=curl/7.51.0, accept=*/*}, response={X-Application-Context=application, Content-Type=text/plain;charset=UTF-8, Content-Length=10, Date=Wed, 29 Mar 2017 20:41:21 GMT, status=200}}}
. Tutaj nie ma treści odpowiedzi.EDYTOWAĆ! Rejestrowanie danych POST
Możesz uzyskać dostęp do danych POST, zastępując WebRequestTraceFilter , ale nie sądzę, że to dobry pomysł (np. Cała przesłana zawartość pliku trafi do dzienników) Oto przykładowy kod, ale nie używaj go:
źródło
TraceRepository
, w jaki sposób możemy uzyskać do tego dostęp?protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
ale nie jestem pewien, kiedy ten filtr jest wykonywany - może być na etapie żądania, więc treść odpowiedzi nie będzie tam gotowa.Ten kod działa dla mnie w aplikacji Spring Boot - wystarczy zarejestrować go jako filtr
źródło
Oto moje rozwiązanie (Spring 2.0.x)
Dodaj zależność od maven:
Zmodyfikuj plik application.properties i dodaj następujący wiersz:
Po uruchomieniu aplikacji do rozruchu wiosennego możesz śledzić ostatnie 100 żądań http, dzwoniąc pod ten adres URL: http: // localhost: 8070 / aktuator / httptrace
źródło
Obecnie Spring Boot ma funkcję aktywatora, aby uzyskać dzienniki żądań i odpowiedzi.
Ale można również uzyskać dzienniki za pomocą Aspect (AOP).
Aspekt oferuje adnotacji, takich jak:
@Before
,@AfterReturning
,@AfterThrowing
itd.@Before
rejestruje żądanie,@AfterReturning
rejestruje odpowiedź i@AfterThrowing
loguje komunikat o błędzie. Być może nie potrzebujesz dziennika wszystkich punktów końcowych, więc możesz zastosować niektóre filtry w pakietach.Oto kilka przykładów :
Na prośbę:
Oto
@Before("within(your.package.where.endpoints.are..*)")
ścieżka pakietu. Wszystkie punkty końcowe w tym pakiecie wygenerują dziennik.Dla odpowiedzi:
Oto
@AfterReturning("within(your.package.where.endpoints.are..*)")
ścieżka pakietu. Wszystkie punkty końcowe w tym pakiecie wygenerują dziennik. RównieżObject returnValue
zawiera odpowiedź.Wyjątek:
Oto
@AfterThrowing(pointcut = ("within(your.package.where.endpoints.are..*)"), throwing = "e")
ścieżka pakietu. Wszystkie punkty końcowe w tym pakiecie wygenerują dziennik.Exception e
Zawiera również odpowiedź na błąd.Oto pełny kod:
Tutaj za pomocą
@ConditionalOnExpression("${endpoint.aspect.enabled:true}")
możesz włączyć / wyłączyć dziennik. po prostu dodajendpoint.aspect.enabled:true
doapplication.property
i kontroluj dziennikWięcej informacji o AOP odwiedź tutaj:
Wiosenne doki o AOP
Przykładowy artykuł o AOP
źródło
new ObjectMapper()
jest drogi, lepiej udostępnij jeden program mapujący dla wszystkichMożna również skonfigurować niestandardowy przechwytywacz sprężynowy w
HandlerInterceptorAdapter
celu uproszczonej implementacji przechwytywaczy tylko przed / po:Następnie rejestrujesz dowolną liczbę przechwytywaczy:
Uwaga: tak jak stwierdzono w @Robert , musisz zwrócić uwagę na konkretne implementacje i aplikacje.
HttpServletRequest
HttpServletResponse
Na przykład w przypadku aplikacji korzystających z
ShallowEtagHeaderFilter
implementacji odpowiedzi będzie aContentCachingResponseWrapper
, więc masz:źródło
Odpowiedź @ hahn wymagała nieco modyfikacji, aby działała dla mnie, ale jest to zdecydowanie najbardziej konfigurowalna rzecz, jaką mogłem uzyskać.
Nie działało to dla mnie, prawdopodobnie dlatego, że mam również HandlerInterceptorAdapter [??], ale wciąż otrzymywałem złą odpowiedź z serwera w tej wersji. Oto moja modyfikacja.
źródło
Jeśli ktoś nadal tego potrzebuje, to prosta implementacja z Spring HttpTrace Actuator. Ale jak powiedzieli górnej, nie rejestruje ciał.
źródło
Rzeczywista odpowiedź znajduje się pod poniższym linkiem https://gist.github.com/int128/e47217bebdb4c402b2ffa7cc199307ba
Wprowadzono pewne zmiany w stosunku do wyżej wymienionego rozwiązania, żądanie i odpowiedź będą logować się w konsoli i pliku, jeśli poziom rejestratora to info. możemy wydrukować w konsoli lub pliku.
Dane wyjściowe w pliku:
źródło
Jeśli widzisz tylko część ładunku żądania, musisz wywołać tę
setMaxPayloadLength
funkcję, ponieważ domyślnie wyświetla tylko 50 znaków w treści żądania. Ustawienie wartościsetIncludeHeaders
false jest dobrym pomysłem, jeśli nie chcesz rejestrować nagłówków uwierzytelniania!źródło
jeśli używasz Tomcat w aplikacji rozruchowej, oto jest
org.apache.catalina.filters.RequestDumperFilter
ścieżka dla ciebie. (ale nie zapewni Ci „wyjątków w jednym miejscu”).źródło
kod wklejony poniżej działa z moimi testami i można go pobrać z mojego [projektu github] [1], udostępniając po zastosowaniu rozwiązania opartego na tym w projekcie produkcyjnym.
źródło
Aby zarejestrować wszystkie żądania z parametrami wejściowymi i treścią, możemy użyć filtrów i przechwytywaczy . Ale podczas korzystania z filtru lub przechwytywacza nie możemy wydrukować treści żądania wiele razy. Lepszym sposobem jest użycie Spring-AOP. Korzystając z tego, możemy odłączyć mechanizm rejestrowania od aplikacji. AOP może być używany do rejestrowania Wejście i wyjście z każdej metody w aplikacji.
Moje rozwiązanie to:
}
źródło
Jeśli masz skonfigurowany serwer Spring Boot Config, po prostu włącz rejestrator debugowania dla klasy:
Http11InputBuffer.Http11InputBuffer.java
Debugowanie rejestruje wszystkie żądania i odpowiedzi dla każdego żądania
źródło
Aby rejestrować żądania, których wynikiem jest tylko 400:
źródło