
Wolverine 0.9.12 just went up on Nuget with new bug fixes, documentation improvements, improved Rabbit MQ usage of topics, local queue usage, and a lot of new functionality around the command line diagnostics. See the whole release notes here.
In this post, I want to zero into “command line diagnostics.” Speaking from a mix of concerns about being both a user of Wolverine and one of the people needing to support other people using Wolverine online, here’s a not exhaustive list of real challenges I’ve already seen or anticipate as Wolverine gets out into the wild more in the near future:
- How is Wolverine configured? What extensions are found?
- What middleware is registered, and is it hooked up correctly?
- How is Wolverine handling a specific message exactly?
- How is Wolverine HTTP handling an HTTP request for a specific route?
- Is Wolverine finding all the handlers? Where is it looking?
- Where is Wolverine trying to send each message?
- Are we missing any configuration items? Is the database reachable? Is the url for a web service proxy in our application valid?
- When Wolverine has to interact with databases or message brokers, are those servers configured correctly to run the application?
That’s a big list of potentially scary issues, so let’s run down a list of command line diagnostic tools that come out of the box with Wolverine to help developers be more productive in real world development. First off, Wolverine’s command line support is all through the Oakton library, and you’ll want to enable Oakton command handling directly in your main application through this line of code at the very end of a typical Program
file:
// This is an extension method within Oakton
// And it's important to relay the exit code
// from Oakton commands to the command line
// if you want to use these tools in CI or CD
// pipelines to denote success or failure
return await app.RunOaktonCommands(args);
You’ll know Oakton is configured correctly if you’ll just go to the command line terminal of your preference at the root of your project and type:
dotnet run -- help
In a simple Wolverine application, you’d get these options out of the box:
The available commands are:
Alias Description
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
check-env Execute all environment checks against the application
codegen Utilities for working with JasperFx.CodeGeneration and JasperFx.RuntimeCompiler
describe Writes out a description of your running application to either the console or a file
help List all the available commands
resources Check, setup, or teardown stateful resources of this system
run Start and run this .Net application
storage Administer the Wolverine message storage
Use dotnet run -- ? [command name] or dotnet run -- help [command name] to see usage help about a specific command
Let me admit that there’s a little bit of “magic” in the way that Wolverine uses naming or type conventions to “know” how to call into your application code. It’s great (in my opinion) that Wolverine doesn’t force you to pollute your code with framework concerns or require you to shape your code around Wolverine’s APIs the way most other .NET frameworks do.
Cool, so let’s move on to…
Describe the Configured Application
The annoying –framework flag is only necessary if your application targets multiple .NET frameworks, but no sane person would ever do that for a real application.
Partially for my own sanity, there’s a lot more support for Wolverine in the describe
command. To see this in usage, consider the sample DiagnosticsApp from the Wolverine codebase. If I use the dotnet run --framework net7.0 -- describe
command from that project, I get this copious textual output.
Just to summarize, what you’ll see in the command line report is:
- “Wolverine Options” – the basics properties as configured, including what Wolverine thinks is the application assembly and any registered extensions
- “Wolverine Listeners” – a tabular list of all the configured listening endpoints, including local queues, within the system and information about how they are configured
- “Wolverine Message Routing” – a tabular list of all the message routing for known messages published within the system
- “Wolverine Sending Endpoints” – a tabular list of all known, configured endpoints that send messages externally
- “Wolverine Error Handling” – a preview of the active message failure policies active within the system
- “Wolverine Http Endpoints” – shows all Wolverine HTTP endpoints. This is only active if WolverineFx.HTTP is used within the system
The latest Wolverine did add some optional message type discovery functionality specifically to make this describe
command be more usable by letting Wolverine know about more message types that will be sent at runtime, but cannot be easily recognized as such strictly from configuration using a mix of marker interface types and/or attributes:
// These are all published messages that aren't
// obvious to Wolverine from message handler endpoint
// signatures
public record InvoiceShipped(Guid Id) : IEvent;
public record CreateShippingLabel(Guid Id) : ICommand;
[WolverineMessage]
public record AddItem(Guid Id, string ItemName);
Environment Checks
Have you ever made a deployment to production just to find out that a database connection string was wrong? Or the credentials to a message broker were wrong? Or your service wasn’t running under an account that had read access to a file share your application needed to scan? Me too!
Wolverine adds several environment checks so that you can use Oakton’s Environment Check functionality to self-diagnose potential configuration issues with:
dotnet run -- check-env
You could conceivably use this as part of your continuous delivery pipeline to quickly verify the application configuration for an application and fail fast & roll back if the checks fail.
How is Wolverine calling my message handlers?!?
Wolverine admittedly involves some “magic” about how it calls into your message handlers, and it’s not unlikely you may be confused about whether or how some kind of registered middleware is working within your system. Or maybe you’re just mildly curious about how Wolverine works at all.
To that end, you can preview — or just generate ahead of time for better “cold starts” — the dynamic source code that Wolverine generates for your message handlers or HTTP handlers with:
dotnet run -- codegen preview
Or just write the code to the file system so you can look at it to your heart’s content with your IDE with:
dotnet run -- codegen write
Which should write the source code files to /Internal/Generated/WolverineHandlers. Here’s a sample from the same diagnostics app sample:
// <auto-generated/>
#pragma warning disable
namespace Internal.Generated.WolverineHandlers
{
public class CreateInvoiceHandler360502188 : Wolverine.Runtime.Handlers.MessageHandler
{
public override System.Threading.Tasks.Task HandleAsync(Wolverine.Runtime.MessageContext context, System.Threading.CancellationToken cancellation)
{
var createInvoice = (IntegrationTests.CreateInvoice)context.Envelope.Message;
var outgoing1 = IntegrationTests.CreateInvoiceHandler.Handle(createInvoice);
// Outgoing, cascaded message
return context.EnqueueCascadingAsync(outgoing1);
}
}
}
Database or Message Broker Setup
Your application will require some configuration of external resources if you’re using any mix of Wolverine’s transactional inbox/outbox support which targets Postgresql or Sql Server or message brokers like Rabbit MQ, Amazon SQS, or Azure Service Bus. Not to worry (too much), Wolverine exposes some command line support for making any necessary configuration setup in these resources with the Oakton resources
command.
In the diagnostics app, we could ensure that our connected Postgresql database has all the necessary schema tables and the Rabbit MQ broker has all the necessary queues, exchanges, and bindings that out application needs to function with:
dotnet run -- resources setup
In testing or normal development work, I may also want to reset the state of these resources to delete now obsolete messages in either the database or the Rabbit MQ queues, and we can fortunately do that with:
dotnet run -- resources clear
There are also resource options for:
teardown
— remove all the database objects or message broker objects that the Wolverine application placed therestatistics
— glean some information about the number of records or messages in the stateful resourcescheck
— do environment checks on the configuration of the stateful resources. This is purely a diagnostic functionlist
— just show you information about the known, stateful resources
Summary
Is any of this wall of textual reports being spit out at the command line sexy? Not in the slightest. Will this functionality help development teams be more productive with Wolverine? Will this functionality help myself and other Wolverine team members support remote users in the future? I’m hopeful that the answer to the first question is “yes” and pretty confident that it’s a “hell, yes” to the second question.
I would also hope that folks see this functionality and agree with my assessment that Wolverine (and Marten) are absolutely appropriate for real life usage and well beyond the toy project phase.
Anyway, more on Wolverine next week starting with an exploration of Wolverine’s local queuing support for asynchronous processing.