Unverified Commit 814facc4 authored by Jens Goldhammer's avatar Jens Goldhammer Committed by GitHub
Browse files

Merge pull request #229 from gmessner/feature/228-add-request-response-logging

Add request and response logging
parents d0ffd09f 12f5d867
......@@ -11,7 +11,7 @@ To utilize the GitLab API for Java in your project, simply add the following dep
```java
dependencies {
...
compile group: 'org.gitlab4j', name: 'gitlab4j-api', version: '4.8.38'
compile group: 'org.gitlab4j', name: 'gitlab4j-api', version: '4.8.39'
}
```
......@@ -20,7 +20,7 @@ dependencies {
<dependency>
<groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId>
<version>4.8.38</version>
<version>4.8.39</version>
</dependency>
```
......@@ -96,6 +96,23 @@ GitLabApi gitLabApi = new GitLabApi(ApiVersion.V3, "http://your.gitlab.server.co
As of GitLab 11.0 support for the GitLab API v3 has been removed (see https://about.gitlab.com/2018/06/01/api-v3-removal-impending/). Support for GitLab API v3 will be removed from this library in January 2019. If you are utilizing the v3 support, please update your code before January 2019.
---
## Logging of API Requests and Responses
As of GitLab4J-API 4.8.39 support has been added to log the requests to and the responses from the
GitLab API. Enable logging using one of the following methods on the GitLabApi instance:
```java
GitLabApi gitLabApi = new GitLabApi("http://your.gitlab.server.com", "YOUR_PRIVATE_TOKEN");
// Log using the shared logger and default level of FINE
gitLabApi.enableRequestResponseLogging();
// Log using the shared logger and the INFO level
gitLabApi.enableRequestResponseLogging(java.util.logging.Level.INFO);
// Log using the specified logger and the INFO level
gitLabApi.enableRequestResponseLogging(youtLoggerInstance, java.util.logging.Level.INFO);
```
---
## Results Paging
GitLab4J-API provides an easy to use paging mechanism to page through lists of results from the GitLab API.
......
......@@ -46,6 +46,7 @@
<junit.version>4.12</junit.version>
<mockito.version>2.19.0</mockito.version>
<hamcrest.version>1.3</hamcrest.version>
<systemRules.version>1.18.0</systemRules.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
......@@ -258,6 +259,12 @@
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<version>${systemRules.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
......@@ -4,6 +4,7 @@ import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.core.MediaType;
......@@ -23,7 +24,7 @@ import org.gitlab4j.api.utils.SecretString;
*/
public class GitLabApi {
private final static Logger LOG = Logger.getLogger(GitLabApi.class.getName());
private final static Logger LOGGER = Logger.getLogger(GitLabApi.class.getName());
/** GitLab4J default per page. GitLab will ignore anything over 100. */
public static final int DEFAULT_PER_PAGE = 100;
......@@ -85,7 +86,7 @@ public class GitLabApi {
* @return the GitLab4J shared Logger instance
*/
public static final Logger getLogger() {
return (LOG);
return (LOGGER);
}
/**
......@@ -638,6 +639,69 @@ public class GitLabApi {
apiClient = new GitLabApiClient(apiVersion, hostUrl, tokenType, authToken, secretToken, clientConfigProperties);
}
/**
* Enable the logging of the requests to and the responses from the GitLab server API
* using the GitLab4J shared Logger instance and Level.FINE as the level.
*
* @return this GitLabApi instance
*/
public GitLabApi withRequestResponseLogging() {
enableRequestResponseLogging();
return (this);
}
/**
* Enable the logging of the requests to and the responses from the GitLab server API
* using the GitLab4J shared Logger instance.
*
* @param level the logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)
* @return this GitLabApi instance
*/
public GitLabApi withRequestResponseLogging(Level level) {
enableRequestResponseLogging(level);
return (this);
}
/**
* Enable the logging of the requests to and the responses from the GitLab server API.
*
* @param logger the Logger instance to log to
* @param level the logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)
* @return this GitLabApi instance
*/
public GitLabApi withRequestResponseLogging(Logger logger, Level level) {
enableRequestResponseLogging(logger, level);
return (this);
}
/**
* Enable the logging of the requests to and the responses from the GitLab server API
* using the GitLab4J shared Logger instance and Level.FINE as the level.
*/
public void enableRequestResponseLogging() {
enableRequestResponseLogging(LOGGER, Level.FINE);
}
/**
* Enable the logging of the requests to and the responses from the GitLab server API
* using the GitLab4J shared Logger instance.
*
* @param level the logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)
*/
public void enableRequestResponseLogging(Level level) {
enableRequestResponseLogging(LOGGER, level);
}
/**
* Enable the logging of the requests to and the responses from the GitLab server API.
*
* @param logger the Logger instance to log to
* @param level the logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)
*/
public void enableRequestResponseLogging(Logger logger, Level level) {
this.apiClient.enableRequestResponseLogging(logger, level);
}
/**
* Sets up all future calls to the GitLab API to be done as another user specified by sudoAsUsername.
* To revert back to normal non-sudo operation you must call unsudo(), or pass null as the username.
......@@ -1413,5 +1477,4 @@ public class GitLabApi {
return wikisApi;
}
}
......@@ -12,6 +12,8 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
......@@ -36,6 +38,7 @@ import org.gitlab4j.api.utils.JacksonJson;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
......@@ -237,6 +240,24 @@ public class GitLabApiClient {
clientConfig.register(MultiPartFeature.class);
}
/**
* Enable the logging of the requests to and the responses from the GitLab server API.
*
* @param logger the Logger instance to log to
* @param level the logging level (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST)
*/
void enableRequestResponseLogging(Logger logger, Level level) {
LoggingFeature loggingFeature = new LoggingFeature(
logger, level, LoggingFeature.Verbosity.PAYLOAD_TEXT, LoggingFeature.DEFAULT_MAX_ENTITY_SIZE);
clientConfig.register(loggingFeature);
// Recreate the Client instance if already created.
if (apiClient != null) {
createApiClient();
}
}
/**
* Get the auth token being used by this client.
*
......@@ -629,18 +650,22 @@ public class GitLabApiClient {
return (invocation(url, queryParams, MediaType.APPLICATION_JSON));
}
protected Client createApiClient() {
ClientBuilder clientBuilder = ClientBuilder.newBuilder().withConfig(clientConfig);
if (ignoreCertificateErrors) {
clientBuilder.sslContext(openSslContext).hostnameVerifier(openHostnameVerifier);
}
apiClient = clientBuilder.build();
return (apiClient);
}
protected Invocation.Builder invocation(URL url, MultivaluedMap<String, String> queryParams, String accept) {
if (apiClient == null) {
if (ignoreCertificateErrors) {
apiClient = ClientBuilder.newBuilder()
.withConfig(clientConfig)
.sslContext(openSslContext)
.hostnameVerifier(openHostnameVerifier)
.build();
} else {
apiClient = ClientBuilder.newBuilder().withConfig(clientConfig).build();
}
createApiClient();
}
WebTarget target = apiClient.target(url.toExternalForm()).property(ClientProperties.FOLLOW_REDIRECTS, true);
......
......@@ -9,6 +9,7 @@ import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.GitLabApiException;
import org.gitlab4j.api.HookManager;
import org.gitlab4j.api.utils.HttpRequestUtils;
......@@ -21,7 +22,7 @@ public class SystemHookManager extends HookManager {
public static final String SYSTEM_HOOK_EVENT = "System Hook";
private final static Logger LOG = Logger.getLogger(SystemHookManager.class.getName());
private final static Logger LOGGER = GitLabApi.getLogger();
private final JacksonJson jacksonJson = new JacksonJson();
// Collection of objects listening for System Hook events.
......@@ -55,27 +56,27 @@ public class SystemHookManager extends HookManager {
if (!isValidSecretToken(request)) {
String message = "X-Gitlab-Token mismatch!";
LOG.warning(message);
LOGGER.warning(message);
throw new GitLabApiException(message);
}
String eventName = request.getHeader("X-Gitlab-Event");
LOG.info("handleEvent: X-Gitlab-Event=" + eventName);
LOGGER.info("handleEvent: X-Gitlab-Event=" + eventName);
if (!SYSTEM_HOOK_EVENT.equals(eventName)) {
String message = "Unsupported X-Gitlab-Event, event Name=" + eventName;
LOG.warning(message);
LOGGER.warning(message);
throw new GitLabApiException(message);
}
try {
SystemHookEvent event;
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(HttpRequestUtils.getShortRequestDump("System Hook", true, request));
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(HttpRequestUtils.getShortRequestDump("System Hook", true, request));
String postData = HttpRequestUtils.getPostDataAsString(request);
LOG.fine("Raw POST data:\n" + postData);
LOGGER.fine("Raw POST data:\n" + postData);
event = jacksonJson.unmarshal(SystemHookEvent.class, postData);
LOG.fine(event.getEventName() + "\n" + jacksonJson.marshal(event) + "\n");
LOGGER.fine(event.getEventName() + "\n" + jacksonJson.marshal(event) + "\n");
} else {
InputStreamReader reader = new InputStreamReader(request.getInputStream());
event = jacksonJson.unmarshal(SystemHookEvent.class, reader);
......@@ -84,7 +85,7 @@ public class SystemHookManager extends HookManager {
fireEvent(event);
} catch (Exception e) {
LOG.warning("Error processing JSON data, exception=" +
LOGGER.warning("Error processing JSON data, exception=" +
e.getClass().getSimpleName() + ", error=" + e.getMessage());
throw new GitLabApiException(e);
}
......@@ -98,10 +99,10 @@ public class SystemHookManager extends HookManager {
*/
public void handleEvent(SystemHookEvent event) throws GitLabApiException {
if (event != null) {
LOG.info("handleEvent:" + event.getClass().getSimpleName() + ", eventName=" + event.getEventName());
LOGGER.info("handleEvent:" + event.getClass().getSimpleName() + ", eventName=" + event.getEventName());
fireEvent(event);
} else {
LOG.warning("handleEvent: provided event cannot be null!");
LOGGER.warning("handleEvent: provided event cannot be null!");
}
}
......@@ -154,7 +155,7 @@ public class SystemHookManager extends HookManager {
fireRepositoryEvent((RepositorySystemHookEvent) event);
} else {
String message = "Unsupported event, event_named=" + event.getEventName();
LOG.warning(message);
LOGGER.warning(message);
throw new GitLabApiException(message);
}
}
......
......@@ -9,6 +9,7 @@ import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.GitLabApiException;
import org.gitlab4j.api.HookManager;
import org.gitlab4j.api.utils.HttpRequestUtils;
......@@ -19,7 +20,7 @@ import org.gitlab4j.api.utils.JacksonJson;
*/
public class WebHookManager extends HookManager {
private final static Logger LOG = Logger.getLogger(WebHookManager.class.getName());
private final static Logger LOGGER = GitLabApi.getLogger();
private final JacksonJson jacksonJson = new JacksonJson();
// Collection of objects listening for WebHook events.
......@@ -53,12 +54,12 @@ public class WebHookManager extends HookManager {
if (!isValidSecretToken(request)) {
String message = "X-Gitlab-Token mismatch!";
LOG.warning(message);
LOGGER.warning(message);
throw new GitLabApiException(message);
}
String eventName = request.getHeader("X-Gitlab-Event");
LOG.info("handleEvent: X-Gitlab-Event=" + eventName);
LOGGER.info("handleEvent: X-Gitlab-Event=" + eventName);
switch (eventName) {
case BuildEvent.BUILD_HOOK_X_GITLAB_EVENT:
......@@ -74,19 +75,19 @@ public class WebHookManager extends HookManager {
default:
String message = "Unsupported X-Gitlab-Event, event Name=" + eventName;
LOG.warning(message);
LOGGER.warning(message);
throw new GitLabApiException(message);
}
try {
Event event;
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(HttpRequestUtils.getShortRequestDump(eventName + " webhook", true, request));
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(HttpRequestUtils.getShortRequestDump(eventName + " webhook", true, request));
String postData = HttpRequestUtils.getPostDataAsString(request);
LOG.fine("Raw POST data:\n" + postData);
LOGGER.fine("Raw POST data:\n" + postData);
event = jacksonJson.unmarshal(Event.class, postData);
LOG.fine(event.getObjectKind() + " event:\n" + jacksonJson.marshal(event) + "\n");
LOGGER.fine(event.getObjectKind() + " event:\n" + jacksonJson.marshal(event) + "\n");
} else {
InputStreamReader reader = new InputStreamReader(request.getInputStream());
event = jacksonJson.unmarshal(Event.class, reader);
......@@ -95,7 +96,7 @@ public class WebHookManager extends HookManager {
fireEvent(event);
} catch (Exception e) {
LOG.warning("Error parsing JSON data, exception=" + e.getClass().getSimpleName() + ", error=" + e.getMessage());
LOGGER.warning("Error parsing JSON data, exception=" + e.getClass().getSimpleName() + ", error=" + e.getMessage());
throw new GitLabApiException(e);
}
}
......@@ -108,7 +109,7 @@ public class WebHookManager extends HookManager {
*/
public void handleEvent(Event event) throws GitLabApiException {
LOG.info("handleEvent: object_kind=" + event.getObjectKind());
LOGGER.info("handleEvent: object_kind=" + event.getObjectKind());
switch (event.getObjectKind()) {
case BuildEvent.OBJECT_KIND:
......@@ -123,7 +124,7 @@ public class WebHookManager extends HookManager {
default:
String message = "Unsupported event object_kind, object_kind=" + event.getObjectKind();
LOG.warning(message);
LOGGER.warning(message);
throw new GitLabApiException(message);
}
......@@ -194,7 +195,7 @@ public class WebHookManager extends HookManager {
default:
String message = "Unsupported event object_kind, object_kind=" + event.getObjectKind();
LOG.warning(message);
LOGGER.warning(message);
throw new GitLabApiException(message);
}
}
......
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Greg Messner <greg@messners.com>
*
* 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.
*/
package org.gitlab4j.api;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.util.logging.Level;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.SystemErrRule;
/**
* In order for these tests to run you must set the following properties in ~/test-gitlab4j.properties
* <p>
* TEST_HOST_URL
* TEST_PRIVATE_TOKEN
* <p>
* If any of the above are NULL, all tests in this class will be skipped.
*/
public class TestRequestResponseLogging {
@Rule
public final SystemErrRule systemErrorRule = new SystemErrRule().enableLog();
// The following needs to be set to your test repository
private static final String TEST_HOST_URL;
private static final String TEST_PRIVATE_TOKEN;
static {
TEST_HOST_URL = TestUtils.getProperty("TEST_HOST_URL");
TEST_PRIVATE_TOKEN = TestUtils.getProperty("TEST_PRIVATE_TOKEN");
}
private static GitLabApi gitLabApi;
private static GitLabApi gitLabApiWithoutLogging;
@BeforeClass
public static void setup() throws GitLabApiException {
String problems = "";
if (TEST_HOST_URL == null || TEST_HOST_URL.trim().isEmpty()) {
problems += "TEST_HOST_URL cannot be empty\n";
}
if (TEST_PRIVATE_TOKEN == null || TEST_PRIVATE_TOKEN.trim().isEmpty()) {
problems += "TEST_PRIVATE_TOKEN cannot be empty\n";
}
if (problems.isEmpty()) {
gitLabApi = new GitLabApi(TEST_HOST_URL, TEST_PRIVATE_TOKEN);
gitLabApi.enableRequestResponseLogging(Level.INFO);
gitLabApiWithoutLogging = new GitLabApi(TEST_HOST_URL, TEST_PRIVATE_TOKEN);
} else {
System.err.print(problems);
}
}
@Before
public void beforeMethod() {
assumeTrue(gitLabApi != null);
assumeTrue(gitLabApiWithoutLogging != null);
}
@Test
public void shouldLogRequests() throws GitLabApiException {
systemErrorRule.clearLog();
gitLabApi.getRunnersApi().getAllRunners();
String log = systemErrorRule.getLog();
assertTrue("Request/response log information was missing.", log.contains("/api/v4/runners"));
}
@Test
public void shouldNotLogRequests() throws GitLabApiException {
systemErrorRule.clearLog();
gitLabApiWithoutLogging.getRunnersApi().getAllRunners();
String log = systemErrorRule.getLog();
assertFalse("Request/response log information was incorrectly present.", log.contains("/api/v4/runners"));
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment