eris
Error handling library with readable stack traces and flexible formatting support 🎆
Top Related Projects
Official Discord API Documentation
A powerful JavaScript library for interacting with the Discord API
An API wrapper for Discord written in Python.
Java wrapper for the popular chat & VOIP service: Discord https://discord.com
An unofficial .Net wrapper for the Discord API (https://discord.com/)
Connects Discord and IRC channels by sending messages back and forth.
Quick Overview
Eris is a powerful and flexible Discord library for Node.js. It provides a robust API for creating Discord bots and applications, with support for both REST and WebSocket interactions. Eris is known for its lightweight design and efficient performance.
Pros
- Lightweight and efficient, with a small memory footprint
- Supports both REST and WebSocket interactions with Discord API
- Extensive documentation and active community support
- Flexible and customizable, allowing for complex bot implementations
Cons
- Steeper learning curve compared to some other Discord libraries
- Less abstraction, which may require more manual handling of certain features
- Limited built-in command handling, requiring additional setup for complex command structures
- Smaller ecosystem of plugins and extensions compared to some alternatives
Code Examples
Creating a simple bot that responds to a command:
const Eris = require('eris');
const bot = new Eris('YOUR_BOT_TOKEN');
bot.on('ready', () => {
console.log('Bot is ready!');
});
bot.on('messageCreate', (msg) => {
if (msg.content === '!ping') {
bot.createMessage(msg.channel.id, 'Pong!');
}
});
bot.connect();
Sending an embed message:
const embed = {
title: 'Example Embed',
description: 'This is an example embed message',
color: 0x7289DA,
fields: [
{ name: 'Field 1', value: 'Value 1', inline: true },
{ name: 'Field 2', value: 'Value 2', inline: true }
]
};
bot.createMessage(channelID, { embed: embed });
Using command middleware:
const Eris = require('eris');
const bot = new Eris('YOUR_BOT_TOKEN');
function checkPermissions(msg, args, next) {
if (msg.member.permissions.has('administrator')) {
next();
} else {
bot.createMessage(msg.channel.id, 'You do not have permission to use this command.');
}
}
bot.registerCommand('admin', (msg, args) => {
bot.createMessage(msg.channel.id, 'Admin command executed!');
}, {
description: 'An admin-only command',
fullDescription: 'This command can only be used by administrators.',
requirements: {
custom: checkPermissions
}
});
bot.connect();
Getting Started
To get started with Eris, follow these steps:
-
Install Eris using npm:
npm install eris
-
Create a new JavaScript file (e.g.,
bot.js
) and add the following code:const Eris = require('eris'); const bot = new Eris('YOUR_BOT_TOKEN'); bot.on('ready', () => { console.log('Bot is ready!'); }); bot.on('messageCreate', (msg) => { if (msg.content === '!hello') { bot.createMessage(msg.channel.id, 'Hello, World!'); } }); bot.connect();
-
Replace
'YOUR_BOT_TOKEN'
with your actual Discord bot token. -
Run the bot using Node.js:
node bot.js
Your bot should now be online and responding to the !hello
command in Discord servers it has been invited to.
Competitor Comparisons
Official Discord API Documentation
Pros of discord-api-docs
- Official documentation directly from Discord, ensuring accuracy and up-to-date information
- Comprehensive coverage of all Discord API features and endpoints
- Includes detailed examples and explanations for developers
Cons of discord-api-docs
- Not a library or implementation, requiring developers to build their own solutions
- May lack practical code examples for specific use cases
- Updates might lag behind actual API changes in some cases
Code Comparison
While discord-api-docs doesn't provide implementation code, it offers detailed API specifications. Eris, on the other hand, provides a JavaScript library for interacting with the Discord API. Here's a brief comparison:
discord-api-docs (API Specification):
{
"name": "username",
"type": 1,
"required": true,
"description": "The user's username"
}
Eris (JavaScript Implementation):
client.createMessage(channelID, {
content: "Hello, Discord!",
embed: {
title: "Embedded Message"
}
});
The discord-api-docs repository focuses on providing detailed API specifications, while Eris offers a practical implementation for interacting with the Discord API in JavaScript.
A powerful JavaScript library for interacting with the Discord API
Pros of discord.js
- Larger community and more extensive documentation
- More frequent updates and active development
- Richer set of features and abstractions
Cons of discord.js
- Higher memory usage and potentially slower performance
- Steeper learning curve for beginners
- More complex codebase
Code Comparison
discord.js:
const { Client, Intents } = require('discord.js');
const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES] });
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.login('token');
Eris:
const Eris = require('eris');
const bot = new Eris('token');
bot.on('ready', () => {
console.log('Ready!');
});
bot.connect();
Both libraries offer similar functionality for creating Discord bots, but discord.js provides a more abstracted and feature-rich API. Eris, on the other hand, aims for simplicity and performance. The code comparison shows that Eris has a slightly more concise setup process, while discord.js requires more configuration but offers more granular control over intents.
An API wrapper for Discord written in Python.
Pros of discord.py
- More mature and widely adopted library for Discord bot development in Python
- Extensive documentation and large community support
- Comprehensive feature set covering most Discord API functionalities
Cons of discord.py
- Steeper learning curve for beginners due to its extensive feature set
- Slightly more verbose code compared to Eris for some common tasks
- Slower development cycle and less frequent updates
Code Comparison
discord.py:
@bot.command()
async def hello(ctx):
await ctx.send(f'Hello, {ctx.author.name}!')
Eris:
client.on('messageCreate', (msg) => {
if (msg.content === '!hello') {
msg.channel.createMessage(`Hello, ${msg.author.username}!`);
}
});
Summary
discord.py is a robust Python library for Discord bot development, offering comprehensive features and strong community support. It's well-suited for complex projects but may have a steeper learning curve. Eris, on the other hand, is a lightweight JavaScript alternative that's easier to get started with but may lack some advanced features. The choice between them often depends on the developer's preferred language and project requirements.
Java wrapper for the popular chat & VOIP service: Discord https://discord.com
Pros of JDA
- Written in Java, offering strong typing and robust object-oriented design
- More extensive and detailed documentation
- Larger community and more frequent updates
Cons of JDA
- Potentially more verbose code due to Java's nature
- Steeper learning curve for developers new to Java
Code Comparison
JDA example:
JDA jda = JDABuilder.createDefault("token")
.addEventListeners(new MessageListener())
.build();
public class MessageListener extends ListenerAdapter {
@Override
public void onMessageReceived(MessageReceivedEvent event) {
if (event.getMessage().getContentRaw().equals("!ping")) {
event.getChannel().sendMessage("Pong!").queue();
}
}
}
Eris example:
const Eris = require("eris");
const bot = new Eris("token");
bot.on("messageCreate", (msg) => {
if(msg.content === "!ping") {
bot.createMessage(msg.channel.id, "Pong!");
}
});
bot.connect();
JDA offers a more structured approach with separate classes for listeners, while Eris provides a more concise, event-driven style typical of JavaScript. JDA's static typing can catch errors at compile-time, whereas Eris relies on runtime checks. The choice between the two often depends on the developer's preferred language and project requirements.
An unofficial .Net wrapper for the Discord API (https://discord.com/)
Pros of Discord.Net
- Written in C#, offering strong typing and integration with .NET ecosystem
- Extensive documentation and active community support
- Supports both synchronous and asynchronous programming models
Cons of Discord.Net
- Larger memory footprint compared to Eris
- Steeper learning curve for developers not familiar with C# or .NET
- May be overkill for simple bot projects
Code Comparison
Eris (JavaScript):
client.on('messageCreate', (msg) => {
if (msg.content === '!ping') {
msg.channel.createMessage('Pong!');
}
});
Discord.Net (C#):
client.MessageReceived += async (s, e) =>
{
if (e.Message.Content == "!ping")
await e.Channel.SendMessageAsync("Pong!");
};
Both libraries offer similar functionality for handling basic bot commands. Eris uses a more concise event-based approach, while Discord.Net leverages C#'s async/await pattern for handling asynchronous operations.
Discord.Net provides a strongly-typed environment, which can help catch errors at compile-time and improve code reliability. However, Eris's JavaScript implementation may be more accessible to developers familiar with Node.js and offers a lower barrier to entry for simple bot projects.
Connects Discord and IRC channels by sending messages back and forth.
Pros of discord-irc
- Specifically designed for bridging Discord and IRC, offering tailored features for this use case
- Supports multiple IRC channels and Discord servers simultaneously
- Includes nickname formatting options for better user identification across platforms
Cons of discord-irc
- Limited to Discord-IRC bridging, less versatile for other chat platforms
- May require more configuration for complex setups compared to Eris
- Less active development and community support
Code Comparison
discord-irc configuration example:
[{
"discord": {
"token": "discord_token"
},
"irc": {
"server": "irc.example.com",
"channel": "#channel"
},
"channelMapping": {
"discord-channel": "#irc-channel"
}
}]
Eris usage example:
const Eris = require("eris");
const bot = new Eris("BOT_TOKEN");
bot.on("ready", () => {
console.log("Ready!");
});
bot.connect();
Summary
discord-irc is a specialized tool for bridging Discord and IRC, offering specific features for this purpose. It supports multiple channels and servers but is limited to these two platforms. Eris, on the other hand, is a more general-purpose Discord library with broader application potential. While discord-irc may be easier to set up for its specific use case, Eris offers more flexibility for custom Discord bot development.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
eris
Package eris
is an error handling library with readable stack traces and flexible formatting support.
go get github.com/rotisserie/eris
- Why you should switch to eris
- Using eris
- Comparison to other packages (e.g. pkg/errors)
- Migrating to eris
- Contributing
Why you should switch to eris
This package was inspired by a simple question: what if you could fix a bug without wasting time replicating the issue or digging through the code? With that in mind, this package is designed to give you more control over error handling via error wrapping, stack tracing, and output formatting.
The example that generated the output below simulates a realistic error handling scenario and demonstrates how to wrap and log errors with minimal effort. This specific error occurred because a user tried to access a file that can't be located, and the output shows a clear path from the top of the call stack to the source.
{
"error":{
"root":{
"message":"error internal server",
"stack":[
"main.main:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:143",
"main.ProcessResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:85",
"main.ProcessResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:82",
"main.GetRelPath:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:61"
]
},
"wrap":[
{
"message":"failed to get relative path for resource 'res2'",
"stack":"main.ProcessResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:85"
},
{
"message":"Rel: can't make ./some/malformed/absolute/path/data.json relative to /Users/roti/",
"stack":"main.GetRelPath:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:61"
}
]
},
"level":"error",
"method":"ProcessResource",
"msg":"method completed with error",
"time":"2020-01-16T11:20:01-05:00"
}
Many of the methods in this package will look familiar if you've used pkg/errors or xerrors, but eris
employs some additional tricks during error wrapping and unwrapping that greatly improve the readability of the stack trace. This package also takes a unique approach to formatting errors that allows you to write custom formats that conform to your error or log aggregator of choice. You can find more information on the differences between eris
and pkg/errors
here.
Using eris
Creating errors
Creating errors is simple via eris.New
.
var (
// global error values can be useful when wrapping errors or inspecting error types
ErrInternalServer = eris.New("error internal server")
)
func (req *Request) Validate() error {
if req.ID == "" {
// or return a new error at the source if you prefer
return eris.New("error bad request")
}
return nil
}
Wrapping errors
eris.Wrap
adds context to an error while preserving the original error.
relPath, err := GetRelPath("/Users/roti/", resource.AbsPath)
if err != nil {
// wrap the error if you want to add more context
return nil, eris.Wrapf(err, "failed to get relative path for resource '%v'", resource.ID)
}
Formatting and logging errors
eris.ToString
and eris.ToJSON
should be used to log errors with the default format (shown above). The JSON method returns a map[string]interface{}
type for compatibility with Go's encoding/json
package and many common JSON loggers (e.g. logrus).
// format the error to JSON with the default format and stack traces enabled
formattedJSON := eris.ToJSON(err, true)
fmt.Println(json.Marshal(formattedJSON)) // marshal to JSON and print
logger.WithField("error", formattedJSON).Error() // or ideally, pass it directly to a logger
// format the error to a string and print it
formattedStr := eris.ToString(err, true)
fmt.Println(formattedStr)
eris
also enables control over the default format's separators and allows advanced users to write their own custom output format.
Interpreting eris stack traces
Errors created with this package contain stack traces that are managed automatically. They're currently mandatory when creating and wrapping errors but optional when printing or logging. By default, the stack trace and all wrapped layers follow the opposite order of Go's runtime
package, which means that the original calling method is shown first and the root cause of the error is shown last.
{
"root":{
"message":"error bad request", // root cause
"stack":[
"main.main:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:143", // original calling method
"main.ProcessResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:71",
"main.(*Request).Validate:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:29", // location of Wrap call
"main.(*Request).Validate:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:28" // location of the root
]
},
"wrap":[
{
"message":"received a request with no ID", // additional context
"stack":"main.(*Request).Validate:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:29" // location of Wrap call
}
]
}
Inverting the stack trace and error output
If you prefer some other order than the default, eris
supports inverting both the stack trace and the entire error output. When both are inverted, the root error is shown first and the original calling method is shown last.
// create a default format with error and stack inversion options
format := eris.NewDefaultStringFormat(eris.FormatOptions{
InvertOutput: true, // flag that inverts the error output (wrap errors shown first)
WithTrace: true, // flag that enables stack trace output
InvertTrace: true, // flag that inverts the stack trace output (top of call stack shown first)
})
// format the error to a string and print it
formattedStr := eris.ToCustomString(err, format)
fmt.Println(formattedStr)
// example output:
// error not found
// main.GetResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:52
// main.ProcessResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:76
// main.main:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:143
// failed to get resource 'res1'
// main.GetResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:52
Inspecting errors
The eris
package provides a couple ways to inspect and compare error types. eris.Is
returns true if a particular error appears anywhere in the error chain. Currently, it works simply by comparing error messages with each other. If an error contains a particular message (e.g. "error not found"
) anywhere in its chain, it's defined to be that error type.
ErrNotFound := eris.New("error not found")
_, err := db.Get(id)
// check if the resource was not found
if eris.Is(err, ErrNotFound) {
// return the error with some useful context
return eris.Wrapf(err, "error getting resource '%v'", id)
}
eris.As
finds the first error in a chain that matches a given target. If there's a match, it sets the target to that error value and returns true.
var target *NotFoundError
_, err := db.Get(id)
// check if the error is a NotFoundError type
if errors.As(err, &target) {
// err is a *NotFoundError and target is set to the error's value
return target
}
eris.Cause
unwraps an error until it reaches the cause, which is defined as the first (i.e. root) error in the chain.
ErrNotFound := eris.New("error not found")
_, err := db.Get(id)
// compare the cause to some sentinel value
if eris.Cause(err) == ErrNotFound {
// return the error with some useful context
return eris.Wrapf(err, "error getting resource '%v'", id)
}
Formatting with custom separators
For users who need more control over the error output, eris
allows for some control over the separators between each piece of the output via the eris.Format
type. If this isn't flexible enough for your needs, see the custom output format section below. To format errors with custom separators, you can define and pass a format object to eris.ToCustomString
or eris.ToCustomJSON
.
// format the error to a string with custom separators
formattedStr := eris.ToCustomString(err, Format{
FormatOptions: eris.FormatOptions{
WithTrace: true, // flag that enables stack trace output
},
MsgStackSep: "\n", // separator between error messages and stack frame data
PreStackSep: "\t", // separator at the beginning of each stack frame
StackElemSep: " | ", // separator between elements of each stack frame
ErrorSep: "\n", // separator between each error in the chain
})
fmt.Println(formattedStr)
// example output:
// error reading file 'example.json'
// main.readFile | .../example/main.go | 6
// unexpected EOF
// main.main | .../example/main.go | 20
// main.parseFile | .../example/main.go | 12
// main.readFile | .../example/main.go | 6
Writing a custom output format
eris
also allows advanced users to construct custom error strings or objects in case the default error doesn't fit their requirements. The UnpackedError
object provides a convenient and developer friendly way to store and access existing error traces. The ErrRoot
and ErrChain
fields correspond to the root error and wrap error chain, respectively. If a root error wraps an external error, that error will be default formatted and assigned to the ErrExternal
field. If any other error type is unpacked, it will appear in the ErrExternal
field. You can access all of the information contained in an error via eris.Unpack
.
// get the unpacked error object
uErr := eris.Unpack(err)
// send only the root error message to a logging server instead of the complete error trace
sentry.CaptureMessage(uErr.ErrRoot.Msg)
Sending error traces to Sentry
eris
supports sending your error traces to Sentry using the Sentry Go client SDK. You can run the example that generated the following output on Sentry UI using the command go run examples/sentry/example.go -dsn=<DSN>
.
*eris.wrapError: test: wrap 1: wrap 2: wrap 3
File "main.go", line 19, in Example
return eris.New("test")
File "main.go", line 23, in WrapExample
err := Example()
File "main.go", line 25, in WrapExample
return eris.Wrap(err, "wrap 1")
File "main.go", line 31, in WrapSecondExample
err := WrapExample()
File "main.go", line 33, in WrapSecondExample
return eris.Wrap(err, "wrap 2")
File "main.go", line 44, in main
err := WrapSecondExample()
File "main.go", line 45, in main
err = eris.Wrap(err, "wrap 3")
Comparison to other packages (e.g. pkg/errors)
Error formatting and stack traces
Readability is a major design requirement for eris
. In addition to the JSON output shown above, eris
also supports formatting errors to a simple string.
failed to get resource 'res1'
main.GetResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:52
error not found
main.main:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:143
main.ProcessResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:76
main.GetResource:/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:52
The eris
error stack is designed to be easier to interpret than other error handling packages, and it achieves this by omitting extraneous information and avoiding unnecessary repetition. The stack trace above omits calls from Go's runtime
package and includes just a single frame for wrapped layers which are inserted into the root error stack trace in the correct order. eris
also correctly handles and updates stack traces for global error values in a transparent way.
The output of pkg/errors
for the same error is shown below. In this case, the root error stack trace is incorrect because it was declared as a global value, and it includes several extraneous lines from the runtime
package. The output is also much more difficult to read and does not allow for custom formatting.
error not found
main.init
/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:18
runtime.doInit
/usr/local/Cellar/go/1.13.6/libexec/src/runtime/proc.go:5222
runtime.main
/usr/local/Cellar/go/1.13.6/libexec/src/runtime/proc.go:190
runtime.goexit
/usr/local/Cellar/go/1.13.6/libexec/src/runtime/asm_amd64.s:1357
failed to get resource 'res1'
main.GetResource
/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:52
main.ProcessResource
/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:76
main.main
/Users/roti/go/src/github.com/rotisserie/eris/examples/logging/example.go:143
runtime.main
/usr/local/Cellar/go/1.13.6/libexec/src/runtime/proc.go:203
runtime.goexit
/usr/local/Cellar/go/1.13.6/libexec/src/runtime/asm_amd64.s:1357
Migrating to eris
Migrating to eris
should be a very simple process. If it doesn't offer something that you currently use from existing error packages, feel free to submit an issue to us. If you don't want to refactor all of your error handling yet, eris
should work relatively seamlessly with your existing error types. Please submit an issue if this isn't the case for some reason.
Many of your dependencies will likely still use pkg/errors for error handling. When external error types are wrapped with additional context, eris
creates a new root error that wraps the original external error. Because of this, error inspection should work seamlessly with other error libraries.
Contributing
If you'd like to contribute to eris
, we'd love your input! Please submit an issue first so we can discuss your proposal.
Released under the MIT License.
Top Related Projects
Official Discord API Documentation
A powerful JavaScript library for interacting with the Discord API
An API wrapper for Discord written in Python.
Java wrapper for the popular chat & VOIP service: Discord https://discord.com
An unofficial .Net wrapper for the Discord API (https://discord.com/)
Connects Discord and IRC channels by sending messages back and forth.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot