<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>dermetfan</title>
  
  <generator>Styx</generator>
  <updated>2022-04-28T00:00:00Z</updated>
  <id>https://dermetfan.net/feed.xml</id>
  <link href="https://dermetfan.net/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="https://dermetfan.net/" rel="alternate"/>
  <author>
  <name>Styx</name>
</author>

  
  
  <entry>
  <id>https://dermetfan.net/posts/distributed-tracing-and-opentelemetry.html</id>
  <title>Monitoring Go Apps with Distributed Tracing and OpenTelemetry</title>
  <updated>2022-04-28T00:00:00Z</updated>
  <link href="https://dermetfan.net/posts/distributed-tracing-and-opentelemetry.html" rel="alternate" type="text/html"/>
  
  <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <ul style="list-style: none;opacity: 0.5;">
<li style="display: inline-block;
padding-right: 2em;
">
    Tags:
</li>
<li style="display: inline-block;
padding-right: 2em;
">
<a href="/tags/code/index.html">
    code
</a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
<a href="/tags/go/index.html">
    go
</a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
<a href="/tags/tracing/index.html">
    tracing
</a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
<a href="/tags/telemetry/index.html">
    telemetry
</a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
<a href="/tags/uptrace/index.html">
    uptrace
</a>
</li>
</ul>

<p><em>The following is a guest article written by <a href="https://github.com/vmihailenco">Vladimir Mihailenco</a> from <a href="https://uptrace.dev">Uptrace</a>.</em></p>

<h1 id="monitoringgoappswithdistributedtracingandopentelemetry">Monitoring Go Apps with Distributed Tracing and OpenTelemetry</h1>

<h2 id="whatistracing">What is tracing?</h2>

<p><a href="https://opentelemetry.uptrace.dev/guide/distributed-tracing.html">Distributed tracing</a> allows to
precisely pinpoint the problem in complex systems, especially those built using a microservices
architecture.</p>

<p>Tracing allow to follow requests as they travel through distributed systems. You get a full context
of what is different, what is broken, and which logs &amp; errors are relevant.</p>

<figure>
<img src="/posts/distributed-tracing-and-opentelemetry/tracing-graph.png" alt="Distributed Tracing" />
<figcaption>Distributed Tracing</figcaption>
</figure>

<p>Using tracing, you can break down requests into
<a href="https://opentelemetry.uptrace.dev/guide/distributed-tracing.html#spans">spans</a>. A <strong>span</strong> is an
operation (unit of work) your app performs handling a request, for example, a database query or a
network call.</p>

<p>A <strong>trace</strong> is a tree of spans that shows the path that a request makes through an app.
The root span is the first span in a trace.</p>

<figure>
<img src="/posts/distributed-tracing-and-opentelemetry/trace-graph.png" alt="Trace" />
<figcaption>Trace</figcaption>
</figure>

<p>To learn more about tracing, see
<a href="https://opentelemetry.uptrace.dev/guide/distributed-tracing.html">Distributed tracing using OpenTelemetry</a>.</p>

<h2 id="whatisopentelemetry">What is OpenTelemetry?</h2>

<p><a href="https://opentelemetry.uptrace.dev/">OpenTelemetry</a> is an open-source observability framework for
<a href="https://opentelemetry.uptrace.dev/guide/distributed-tracing.html">distributed tracing</a> (including
logs and errors) and <a href="https://opentelemetry.uptrace.dev/guide/metrics.html">metrics</a>.</p>

<p>Otel allows developers to collect and export telemetry data in a vendor agnostic way. With
OpenTelemetry, you can <a href="https://opentelemetry.uptrace.dev/instrumentations/">instrument</a> your
application once and then add or change vendors without changing the instrumentation, for example,
here is a list <a href="https://get.uptrace.dev/compare/datadog-competitors.html">popular DataDog alternatives</a> that support
OpenTelemetry.</p>

<h2 id="howtouseopentelemetry">How to use OpenTelemetry?</h2>

<p>OpenTelemetry is available for most programming languages and provides interoperability across
different languages and environments.</p>

<p>You can <a href="https://opentelemetry.uptrace.dev/">get started with OpenTelemetry</a> by following these 5 steps:</p>

<ul>
<li><p><strong>Step 1</strong>. Install a
<a href="https://get.uptrace.dev/compare/distributed-tracing-tools.html">distributed tracing tool</a>.</p></li>
<li><p><strong>Step 2</strong>. Browse
<a href="https://opentelemetry.uptrace.dev/instrumentations/">OpenTelemetry instrumentations</a> to find
examples for your framework and libraries.</p></li>
<li><p><strong>Step 3</strong>. <a href="https://docs.uptrace.dev/#getting-started">Configure</a> the OpenTelemetry SDK to export
telemetry data to the tracing tool.</p></li>
<li><p><strong>Step 4</strong>. Learn about
<a href="https://opentelemetry.uptrace.dev/guide/distributed-tracing.html#what-s-next">OpenTelemetry Tracing API</a>
for your programming language to create custom instrumentations.</p></li>
<li><p><strong>Step 5</strong>. Install
<a href="https://opentelemetry.uptrace.dev/guide/collector.html">OpenTelemetry Collector</a> to gather
<a href="https://opentelemetry.uptrace.dev/guide/collector.html#host-metrics">infrastructure metrics</a>.</p></li>
</ul>

<h2 id="opentelemetrygoapi">OpenTelemetry Go API</h2>

<p>You can create spans using
<a href="https://opentelemetry.uptrace.dev/guide/go-tracing.html">OpenTelemetry Go API</a> like this:</p>

<pre><code class="go">import &quot;go.opentelemetry.io/otel&quot;

var tracer = otel.Tracer(&quot;app_or_package_name&quot;)

func someFunc(ctx context.Context) error {
	ctx, span := tracer.Start(ctx, &quot;some-func&quot;)
	defer span.End()

	// the code you are measuring

	return nil
}
</code></pre>

<p>You can also record
<a href="https://opentelemetry.uptrace.dev/guide/distributed-tracing.html#attributes">attributes</a> and
errors:</p>

<pre><code class="go">func someFunc(ctx context.Context) error {
	ctx, span := tracer.Start(ctx, &quot;some-func&quot;)
	defer span.End()

	if span.IsRecording() {
		span.SetAttributes(
			attribute.Int64(&quot;enduser.id&quot;, userID),
			attribute.String(&quot;enduser.email&quot;, userEmail),
		)
	}

	if err := someOtherFunc(ctx); err != nil {
		span.RecordError(err)
		span.SetStatus(codes.Error, err.Error())
	}

	return nil
}
</code></pre>

<h2 id="whatisuptrace">What is Uptrace?</h2>

<p><a href="https://uptrace.dev/">Uptrace</a> is an open source
<a href="https://get.uptrace.dev/compare/datadog-competitors.html">DataDog alternative</a> that helps developers pinpoint failures and
find performance bottlenecks. Uptrace can process billions of spans on a single server and allows to
monitor your software at 10x lower cost.</p>

<p>Uptrace accepts data from OpenTelemetry and stores it in a ClickHouse database. ClickHouse is a
columnar database that is much more efficient for traces and logs than, for example, Elastic Search.
On the same hardware, ClickHouse can store 10x more traces and, at the same time, provide much
better performance.</p>

<p>You can <a href="https://get.uptrace.dev/guide/opentelemetry-tracing-tool.html">install Uptrace</a> by downloading a DEB/RPM package
or a pre-compiled binary.</p>

<figure>
<img src="/posts/distributed-tracing-and-opentelemetry/uptrace.png" alt="Uptrace tracing tool" />
<figcaption>Uptrace tracing tool</figcaption>
</figure>

<h2 id="whatsnext">What&#8217;s next?</h2>

<p>Next, you can continue exploring <a href="https://opentelemetry.uptrace.dev">OpenTelemetry</a> or start
instrumenting your app using popular instrumentations:</p>

<ul>
<li><a href="https://get.uptrace.dev/opentelemetry/gin-gorm.html">OpenTelemetry guide for Gin and GORM</a></li>
<li><a href="https://opentelemetry.uptrace.dev/instrumentations/go-grpc.html">gRPC OpenTelemetry</a></li>
<li><a href="https://bun.uptrace.dev/guide/performance-monitoring.html">Golang ORM OpenTelemetry</a></li>
<li><a href="https://redis.uptrace.dev/guide/go-redis-monitoring.html">Go Redis OpenTelemetry</a></li>
<li><a href="https://opentelemetry.uptrace.dev/instrumentations/go-zap.html">Zap OpenTelemetry</a></li>
</ul>

<div id="disqus_thread"></div>
<script>
/**
*RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
var disqus_config = function () {
this.page.identifier = "/posts/distributed-tracing-and-opentelemetry";
this.page.url = "https://dermetfan.net/posts/distributed-tracing-and-opentelemetry";
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = '//dermetfan-blog.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>


      </div>
    </content>
</entry>

<entry>
  <id>https://dermetfan.net/posts/zig-with-c-web-servers.html</id>
  <title>Trying Zig with C web servers</title>
  <updated>2020-03-26T00:00:00Z</updated>
  <link href="https://dermetfan.net/posts/zig-with-c-web-servers.html" rel="alternate" type="text/html"/>
  
  <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <div class="paragraph">
<p><ul style="list-style: none;opacity: 0.5;">
  <li style="display: inline-block;
padding-right: 2em;
">
    Tags:
  </li>
<li style="display: inline-block;
padding-right: 2em;
">
  <a href="/tags/code/index.html">
    code
  </a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
  <a href="/tags/zig/index.html">
    zig
  </a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
  <a href="/tags/nix/index.html">
    nix
  </a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
  <a href="/tags/h2o/index.html">
    h2o
  </a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
  <a href="/tags/facil.io/index.html">
    facil.io
  </a>
</li>
<li style="display: inline-block;
padding-right: 2em;
">
  <a href="/tags/lwan/index.html">
    lwan
  </a>
</li>
</ul></p>
</div>
<div class="paragraph">
<p>I wanted to try using Zig to build an application server.</p>
</div>
<div class="paragraph">
<p>The C integration should, in theory, allow me to use an existing web server library.
Here are my results.</p>
</div>
<div class="paragraph">
<p>I tried the following web servers:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#_h2o">H2O</a> is usable with some workarounds.</p>
</li>
<li>
<p><a href="#_facil_io">facil.io</a> works in principle but is unusable in practice.</p>
</li>
<li>
<p><a href="#_lwan">LWAN</a> was a complete failure.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><em>TL;DR: C translation is obviously not production ready, but I was able to get it to work with H2O.</em></p>
</div>
<hr/>
<div class="sect1">
<h2 id="_h2o"><a class="anchor" href="#_h2o"></a><a href="https://h2o.examp1e.net">H2O</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>This is the only one I had success with.</p>
</div>
<div class="paragraph">
<p>I ported the <code>simple.c</code> example to Zig, following this <a href="https://powerdns.org/libh2o/">intro</a> to <code>libh2o</code>.</p>
</div>
<div class="paragraph">
<p><a href="/posts/zig-with-c-web-servers/h2o.tar.gz">Here</a> you can download the entire project.</p>
</div>
<div class="paragraph">
<p>A few problems came up which I want to briefly discuss below.</p>
</div>
<div class="sect2">
<h3 id="_zig_cannot_translate_c_bitfields"><a class="anchor" href="#_zig_cannot_translate_c_bitfields"></a>Zig cannot translate C bitfields</h3>
<div class="paragraph">
<p>H2O makes use of C bitfields throughout its codebase. Unfortunately, Zig does not (yet) understand them.
There was an effort to fix this but it seems to have stagnated:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>issue <a href="https://github.com/ziglang/translate-c/issues/179">#179</a> "translate-c: support C bitfields" (open)</p>
</li>
<li>
<p>PR <a href="https://github.com/ziglang/zig/pull/4165">#4165</a> "Add support for C bitfields" (closed)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Consequently we have to remove all bitfields from H2O.
Fortunately using Nix it is trivial to apply a <a href="/posts/zig-with-c-web-servers/h2o/h2o-no-bitfields.patch">patch</a> and use the recompiled H2O:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-nix" data-lang="nix">pkgs.h2o.overrideAttrs (oldAttrs: {
  patches = [ ./h2o-no-bitfields.patch ];
});</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_libuv"><a class="anchor" href="#_libuv"></a>libuv</h3>
<div class="paragraph">
<p>H2O has its own event loop implementation that is faster than libuv, so for a simple demo I did not want to link against libuv.
So without libuv in the build environment obviously the header file cannot be found.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>./src/c.zig:1:20: error: C import failed
pub usingnamespace @cImport({
                   ^
/nix/store/…-h2o-2.3.0-beta2-dev/include/h2o/socket/uv-binding.h:27:10: note: 'uv.h' file not found
#include &lt;uv.h&gt;</pre>
</div>
</div>
<div class="paragraph">
<p>We can fix that be setting the <code>H2O_USE_LIBUV</code> preprocessor variable.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-zig" data-lang="zig">pub usingnamespace @cImport({
    @cDefine("H2O_USE_LIBUV", "0");
    @cInclude("h2o.h");
});</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you want to compile H2O without libuv support you need to pass that to H2O&#8217;s <code>CMakeLists.txt</code>. Using Nix that looks like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-nix" data-lang="nix">pkgs.h2o.overrideAttrs (oldAttrs: {
  cmakeFlags = [ "-DDISABLE_LIBUV=1" ];
});</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_define_h2o_strlit"><a class="anchor" href="#_define_h2o_strlit"></a><code>#define H2O_STRLIT</code></h3>
<div class="paragraph">
<p>H2O passes strings around with an additional length parameter using the preprocessor.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-c" data-lang="c">h2o_iovec_init(H2O_STRLIT("default");</code></pre>
</div>
</div>
<div class="paragraph">
<p>That expands to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-c" data-lang="c">h2o_iovec_init("default", 7)</code></pre>
</div>
</div>
<div class="paragraph">
<p>That <code>H2O_STRLIT</code> macro cannot be used in Zig code, so we will have to type the length ourselves.</p>
</div>
<div class="paragraph">
<p>For common use cases you can write little wrapper functions to make it more comfortable:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-zig" data-lang="zig">pub fn iovec_init(data: var) h2o_iovec_t {
    return h2o_iovec_init(data, data.len);
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_zig_stdlib_bug_when_linking_libc"><a class="anchor" href="#_zig_stdlib_bug_when_linking_libc"></a>Zig stdlib bug when linking libc</h3>
<div class="paragraph">
<p>Setting up the listening socket is easier than in C. Zig&#8217;s standard library handles everything for us:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-zig" data-lang="zig">var server = std.net.StreamServer.init(.{});
defer server.deinit();
try server.listen(try std.net.Address.parseIp4("127.0.0.1", 12345));</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here I hit issue <a href="https://github.com/ziglang/zig/issues/4797">#4797</a> in the standard library, but that was quickly solved by a Zig contributor.</p>
</div>
<div class="paragraph">
<p>In case you are using a version before commit <code>778dbc17acce77588a46a27074ecb8b418786d94</code>, there is a workaround.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Make a copy of the stdlib.</p>
</li>
<li>
<p>Replace the <code>setsockopt()</code> signature in <code>std/os.zig</code>:</p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-patch" data-lang="patch">- pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
+ pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []u8) SetSockOptError!void {</code></pre>
</div>
</div>
</li>
<li>
<p>Use a compiler binary that is at a path relative to your stdlib:</p>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Zig locates lib files relative to executable path by searching up the filesystem tree for a sub-path of <code>lib/zig/std/std.zig</code> or <code>lib/std/std.zig</code>. Typically the former is an install and the latter a git working tree which contains the build directory.</p>
</div>
</blockquote>
<div class="attribution">
&#8212; <a href="https://github.com/ziglang/zig/blob/master/CONTRIBUTING.md#editing-source-code">Contribution Guidelines</a>
</div>
</div>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_h2o_token"><a class="anchor" href="#_h2o_token"></a>H2O_TOKEN_…</h3>
<div class="paragraph">
<p>Similarly Zig can not parse the <code>H2O_TOKEN_…</code> macros. There is a <code>h2o_lookup_token()</code> function that we can use though.</p>
</div>
<div class="paragraph">
<p>To avoid looking up tokens all the time, we can assign all the tokens on startup of our program to our own constants.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-zig" data-lang="zig">// lookup_token() cannot run comptime so these have to be vars...
pub var TOKEN_CONTENT_LENGTH: *const h2o_token_t;
pub var TOKEN_CONTENT_LOCATION: *const h2o_token_t;
pub var TOKEN_CONTENT_RANGE: *const h2o_token_t;
pub var TOKEN_CONTENT_TYPE: *const h2o_token_t;
// …

pub fn _init() void {
    TOKEN_CONTENT_LENGTH = lookup_token("content-length");
    TOKEN_CONTENT_LOCATION = lookup_token("content-location");
    TOKEN_CONTENT_RANGE = lookup_token("content-range");
    TOKEN_CONTENT_TYPE = lookup_token("content-type");
    // …
}

pub fn lookup_token(name: []const u8) *const h2o_token_t {
    return h2o_lookup_token(&amp;name[0], name.len);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Admittedly that is not very nice. Maybe we could build the <code>H2O_TOKEN_…</code> structs directly and assign them to actual <code>const</code>s.</p>
</div>
</div>
<div class="sect2">
<h3 id="_converting_h2o_iovec_t_to_slices"><a class="anchor" href="#_converting_h2o_iovec_t_to_slices"></a>Converting <code>h2o_iovec_t</code> to slices</h3>
<div class="paragraph">
<p>We can simplify here with a little helper.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-zig" data-lang="zig">pub fn _from_iovec(data: h2o_iovec_t) []u8 {
    return data.base[0..data.len];
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>That allows us to use <code>std.mem.eql()</code> instead of <code>h2o_ismem()</code>, if desired.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-zig" data-lang="zig">if (!std.mem.eql(u8, c.h2o._from_iovec(req.*.method), c.h2o.METHOD_GET)) return -1;</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_conclusion"><a class="anchor" href="#_conclusion"></a>Conclusion</h3>
<div class="paragraph">
<p>Since this was the only web server I could get to work, there is still work to do in the C translation.
But that is to be expected from such an early version of the language.</p>
</div>
<div class="paragraph">
<p>I find it remarkable that C translation works so well already. Very promising!</p>
</div>
<hr/>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_facil_io"><a class="anchor" href="#_facil_io"></a><a href="http://facil.io">facil.io</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>I was able to start the server and answer a request.</p>
</div>
<div class="paragraph">
<p>Unfortunately Zig is unable to translate <code>fiobj_free()</code>.
Therefore I did not think it was worth investigating further.</p>
</div>
<div class="paragraph">
<p><a href="/posts/zig-with-c-web-servers/facil.io.tar.gz">Download</a> all files.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-zig" data-lang="zig">const std = @import("std");

const c = @cImport({
    @cInclude("fio.h");
    @cInclude("http.h");
});

pub fn main() !void {
    const HTTP_HEADER_X_DATA: c.FIOBJ = c.fiobj_str_new("X-Data", "X-Data".len);
    defer c.fiobj_free(HTTP_HEADER_X_DATA); // FIXME https://github.com/ziglang/zig/blob/8ea0a00f406bb04c08a8fa4471c3a3895f82b24a/src-self-hosted/translate_c.zig

    const socket_uuid = c.http_listen("3000", null, .{
        .on_request = on_request,
        .log = 1,
        .on_upgrade = null,
        .on_response = null,
        .on_finish = null,
        .udata = null,
        .public_folder = null,
        .public_folder_length = 0,
        .max_header_size = 0,
        .max_body_size = 0,
        .max_clients = 0,
        .tls = null,
        .reserved1 = 0,
        .reserved2 = 0,
        .reserved3 = 0,
        .ws_max_msg_size = 0,
        .timeout = 0,
        .ws_timeout = 0,
        .is_client = 0,
    });
    if (socket_uuid == -1) return error.Listen;

    c.fio_start(.{ .threads = 1, .workers = 1, });
}

fn on_request(request: [*c]c.http_s) callconv(.C) void {
    const body_ = "Hello World!\r\n";
    var body: [body_.len]u8 = undefined;
    std.mem.copy(u8, body[0..], body_[0..]);

    _ = c.http_send_body(request, body[0..], body.len);
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre>./zig-cache/o/…/cimport.zig:2918:24: error: unable to translate function
pub const fiobj_free = @compileError("unable to translate function");
                       ^
./src/main.zig:8:12: note: referenced here
    defer c.fiobj_free(HTTP_HEADER_X_DATA);
           ^</pre>
</div>
</div>
<hr/>
</div>
</div>
<div class="sect1">
<h2 id="_lwan"><a class="anchor" href="#_lwan"></a><a href="https://lwan.ws">LWAN</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Those macros can clearly not easily be translated to Zig, so the journey ends abruptly.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-c" data-lang="c">#define LWAN_HANDLER_REF(name_) lwan_handler_##name_
#define LWAN_HANDLER(name_)                                                    \
    static enum lwan_http_status lwan_handler_##name_(                         \
        struct lwan_request *, struct lwan_response *, void *);                \
    static const struct lwan_handler_info                                      \
        __attribute__((used, section(LWAN_SECTION_NAME(lwan_handler))))        \
            lwan_handler_info_##name_ = {.name = #name_,                       \
                                         .handler = lwan_handler_##name_};     \
    static enum lwan_http_status lwan_handler_##name_(                         \
        struct lwan_request *request __attribute__((unused)),                  \
        struct lwan_response *response __attribute__((unused)),                \
        void *data __attribute__((unused)))</code></pre>
</div>
</div>
<hr/>
<div id="disqus_thread"></div>
<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
var disqus_config = function () {
  this.page.identifier = "/posts/zig-with-c-web-servers";
  this.page.url = "https://dermetfan.net/posts/zig-with-c-web-servers";
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = '//dermetfan-blog.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
</div>

      </div>
    </content>
</entry>

</feed>
