Skip to content

Commit 4b27121

Browse files
Add WebhookErrorHandler (#59)
Bump version to 0.8.0
1 parent 8f2b640 commit 4b27121

File tree

4 files changed

+112
-6
lines changed

4 files changed

+112
-6
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
[license]: https://img.shields.io/badge/License-Apache%202.0-lightgrey.svg
44
[license-file]: https://github.com/MinnDevelopment/discord-webhooks/blob/master/LICENSE
55

6+
[WebhookClient#setErrorHandler]: https://minndevelopment.github.io/discord-webhooks/club/minnced/discord/webhook/WebhookClient.html#setErrorHandler(club.minnced.discord.webhook.util.WebhookErrorHandler)
7+
[WebhookClient#setDefaultErrorHandler]: https://minndevelopment.github.io/discord-webhooks/club/minnced/discord/webhook/WebhookClient.html#setDefaultErrorHandler(club.minnced.discord.webhook.util.WebhookErrorHandler)
8+
69
[ ![version] ][download]
710
[ ![license] ][license-file]
811

@@ -137,6 +140,25 @@ try (WebhookClient client = WebhookClient.withUrl(url)) {
137140
webhookCluster.close(); // closes each client and can be used again
138141
```
139142

143+
## Error Handling
144+
145+
By default, this library will log every exception encountered when sending a message using the SLF4J logger implementation.
146+
This can be configured using [WebhookClient#setErrorHandler] to custom behavior per client or [WebhookClient#setDefaultErrorHandler] for all clients.
147+
148+
### Example
149+
150+
```java
151+
WebhookClient.setDefaultErrorHandler((client, message, throwable) -> {
152+
System.err.printf("[%s] %s%n", client.getId(), message);
153+
if (throwable != null)
154+
throwable.printStackTrace();
155+
// Shutdown the webhook client when you get 404 response (may also trigger for client#edit calls, be careful)
156+
if (throwable instanceof HttpException ex && ex.getCode() == 404) {
157+
client.close();
158+
}
159+
});
160+
```
161+
140162
## External Libraries
141163

142164
This library also supports sending webhook messages with integration from other libraries such as

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ plugins {
1111
}
1212

1313
val major = "0"
14-
val minor = "7"
15-
val patch = "5"
14+
val minor = "8"
15+
val patch = "0"
1616

1717
group = "club.minnced"
1818
version = "$major.$minor.$patch"

src/main/java/club/minnced/discord/webhook/WebhookClient.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import club.minnced.discord.webhook.send.WebhookMessage;
2525
import club.minnced.discord.webhook.send.WebhookMessageBuilder;
2626
import club.minnced.discord.webhook.util.ThreadPools;
27+
import club.minnced.discord.webhook.util.WebhookErrorHandler;
2728
import okhttp3.OkHttpClient;
2829
import okhttp3.RequestBody;
2930
import okhttp3.Response;
@@ -55,6 +56,7 @@ public class WebhookClient implements AutoCloseable {
5556
/** User-Agent used for REST requests */
5657
public static final String USER_AGENT = "Webhook(https://github.com/MinnDevelopment/discord-webhooks, " + LibraryInfo.VERSION + ")";
5758
private static final Logger LOG = LoggerFactory.getLogger(WebhookClient.class);
59+
private static WebhookErrorHandler DEFAULT_ERROR_HANDLER = WebhookErrorHandler.DEFAULT;
5860

5961
protected final WebhookClient parent;
6062

@@ -70,6 +72,7 @@ public class WebhookClient implements AutoCloseable {
7072
protected long defaultTimeout;
7173
protected volatile boolean isQueued;
7274
protected boolean isShutdown;
75+
protected WebhookErrorHandler errorHandler = DEFAULT_ERROR_HANDLER;
7376

7477
protected WebhookClient(
7578
final long id, final String token, final boolean parseMessage,
@@ -102,6 +105,21 @@ protected WebhookClient(final WebhookClient parent, final long threadId) {
102105
this.isQueued = false;
103106
}
104107

108+
/**
109+
* Configures which default error handler to use instead of {@link WebhookErrorHandler#DEFAULT}.
110+
*
111+
* <p>You can use this to avoid having to set the error handler on each new webhook client instance.
112+
*
113+
* @param handler
114+
* The error handler to use
115+
*
116+
* @throws NullPointerException
117+
* If null is povided
118+
*/
119+
public static void setDefaultErrorHandler(@NotNull WebhookErrorHandler handler) {
120+
DEFAULT_ERROR_HANDLER = Objects.requireNonNull(handler, "Error Handler must not be null!");
121+
}
122+
105123
/**
106124
* Factory method to create a basic WebhookClient with the provided id and token.
107125
*
@@ -238,6 +256,23 @@ public WebhookClient setTimeout(@Nonnegative long millis) {
238256
return this;
239257
}
240258

259+
/**
260+
* Configures the error handling behavior used for all asynchronous send/edit/delete calls.
261+
*
262+
* @param handler
263+
* The error handler
264+
*
265+
* @throws java.lang.NullPointerException
266+
* If provided with null
267+
*
268+
* @return The current WebhookClient instance
269+
*/
270+
@NotNull
271+
public WebhookClient setErrorHandler(@NotNull WebhookErrorHandler handler) {
272+
this.errorHandler = Objects.requireNonNull(handler, "Error Handler must not be null!");
273+
return this;
274+
}
275+
241276
/**
242277
* The current timeout configured by {@link #setTimeout(long)}.
243278
* <br>If no timeout was configured, this returns 0.
@@ -795,7 +830,7 @@ private boolean executePair(@Async.Execute Request req) {
795830
}
796831
else if (!response.isSuccessful()) {
797832
final HttpException exception = failure(response);
798-
LOG.error("Sending a webhook message failed with non-OK http response", exception);
833+
errorHandler.handle(this, "Sending a webhook message failed with non-OK http response", exception);
799834
queue.poll().future.completeExceptionally(exception);
800835
return true;
801836
}
@@ -812,7 +847,7 @@ else if (!response.isSuccessful()) {
812847
}
813848
}
814849
catch (JSONException | IOException e) {
815-
LOG.error("There was some error while sending a webhook message", e);
850+
errorHandler.handle(this, "There was some error while sending a webhook message", e);
816851
queue.poll().future.completeExceptionally(e);
817852
}
818853
return true;
@@ -832,7 +867,7 @@ public String getMethod() {
832867
}
833868
}
834869

835-
protected static final class Bucket {
870+
protected final class Bucket {
836871
public static final int RATE_LIMIT_CODE = 429;
837872
public long resetTime;
838873
public int remainingUses;
@@ -899,7 +934,7 @@ public void update(Response response) {
899934
update0(response);
900935
}
901936
catch (Exception ex) {
902-
LOG.error("Could not read http response", ex);
937+
errorHandler.handle(WebhookClient.this, "Could not read http response", ex);
903938
}
904939
}
905940
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2018-2020 Florian Spieß
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package club.minnced.discord.webhook.util;
18+
19+
import club.minnced.discord.webhook.WebhookClient;
20+
import org.jetbrains.annotations.NotNull;
21+
import org.jetbrains.annotations.Nullable;
22+
import org.slf4j.LoggerFactory;
23+
24+
/**
25+
* Used to dynamically handle errors for webhook requests in {@link WebhookClient}
26+
* <br>If not explicitly configured, this uses {@link #DEFAULT}.
27+
*
28+
* @see WebhookClient#setDefaultErrorHandler(WebhookErrorHandler)
29+
* @see WebhookClient#setErrorHandler(WebhookErrorHandler)
30+
*/
31+
@FunctionalInterface
32+
public interface WebhookErrorHandler {
33+
/**
34+
* The default error handling which simply logs the exception using SLF4J
35+
*/
36+
WebhookErrorHandler DEFAULT = (client, message, throwable) -> LoggerFactory.getLogger(WebhookClient.class).error(message, throwable);
37+
38+
/**
39+
* Implements error handling, must not throw anything!
40+
*
41+
* @param client
42+
* The {@link WebhookClient} instance which encountered the exception
43+
* @param message
44+
* The context message used for logging
45+
* @param throwable
46+
* The encountered exception, or null if the error is only a context message
47+
*/
48+
void handle(@NotNull WebhookClient client, @NotNull String message, @Nullable Throwable throwable);
49+
}

0 commit comments

Comments
 (0)