Relatively Ignorant A blog on things about which I may or may not know stuff069ce0f8b975b6fce829d95e17bc1765bfc32407

Announcing blog posts on Twitter using IFTTT

|

When you have a blog you usually want to let your followers know when a new article is posted. This blog has an Atom feed but RSS readers are less common than they used to be and seem a bit old fashioned.

Announce on Twitter

Announcing a new blog post on my Twitter account seems like the modern way to go. But how?

A long-term solution will probably be to use a post-commit or post-push hook in GitHub where this blog is hosted.

Quick solution: IFTTT

I remembered that I have an account at If This Then That and decided to try using that. It is already linked to my Twitter account so that part of the process was ready to go. I created a new Applet (IFTTT-speak for a set of rules that link services together; formerly called a Recipe) to check the Atom feed periodically and tweet an announcement.

But IFTTT showed the Applet as never run and did not display any error messages that I could find.

Invalid Atom feed

IFTTT help pages include a link to the W3C Feed Validation Service. Using that showed the feed to be invalid because the email address field was empty. This was solved by adding author.email to _config.yml. (I used a valid email address that I can get my mail provider to filter if it starts attracting spam.)

There were other, minor issues, including the link information showing http://blog.michaelstrasser.com instead of https://blog.michaelstrasser.com. I don’t know why Jekyll does that, but a solution is to add an explicit url value to the configuration file:

url:  https://blog.michaelstrasser.com

(This value is only used in Jekyll production mode. In the default development mode it is overwritten by the actual URL: http://localhost:4000 by default.)

Success

Update: The tweet for this page is:

Example tweet sent by IFTTT

By default, IFTTT uses its own link shortener, but I turned that off as described here.

Stopping code samples from wrapping

|

I needed to tweak the CSS for this blog so code samples do not wrap.

The goal

I wanted long lines like this one to not wrap, and be scrollable when they are too long:

    private static final Pattern REQUEST_PATTERN = Pattern.compile("^/v1/workflow/(?<crumbId>[0-9a-f]+)$");

They are converted by Jekyll into pre elements but the Poole and Lanyon styling causes them to wrap.

First solution

My blog stylesheet is read after those of Poole and Lanyon and contained:

pre {
  white-space: pre;
  overflow: auto;
  font-size: 0.7rem;
}
  • white-space: pre prevents word wrapping
  • overflow: auto causes too-long lines to be hidden instead of wrapped and scroll bars provided
  • font-size: 0.7em tweaks the font a bit to my liking

This worked in Chrome and Firefox but not in Safari, which was still wrapping code.

The Safari solution

I found that poole.css contains word-wrap: break-word in its pre rule. It appears that Safari places word-wrap at higher precedence than overflow.

So my blog stylesheet now includes this for code samples, and works in Safari as well:

pre {
  white-space: pre;
  overflow: auto;
  font-size: 0.7rem;
  word-wrap: unset;
}

NB: Is higher precedence the correct CSS term in this case? If you know, please let me know.

Using the Spring Cloud Sleuth trace ID

|

Here is a way to interact with Spring Cloud Sleuth in an API service that works with an asynchronous workflow.

Introduction

On a recent project we implemented an API service using Spring Boot with two operations:

  1. initiate a new, asynchronous workflow
  2. report on the status of an existing workflow

Each workflow is identified by a unique ID that is generated by the service on the first call and returned to the consumer. The consumer then uses that ID in subsequent status requests to identify the workflow.

Spring Cloud Sleuth

The service is one of a mesh of microservices and we added Spring Cloud Sleuth to provide tracing across microservice calls. In Sleuth, a Span is a basic unit of work, and spans are linked together into a Trace for a single logical transaction. The Sleuth Reference provides much more detail, including this useful diagram:

Spring Cloud Sleuth: Span and Trace visualisations
Spring Cloud Sleuth: Span and Trace visualisations

Use the Sleuth trace ID for each workflow

Instead of generating a separate ID (such as a UUID) we decided to take the trace ID from Sleuth. Sleuth generates a random long integer as a trace ID at the beginning of the request and passes that as part of requests to other services.

Part of our rationale for using the trace ID is to reduce log message clutter. When Sleuth is included, the IDs are inserted into the Spring logs via the SLF4J MDC. Here is a simple Spring Boot log message with information added by Sleuth:

2017-07-14 13:32:59.094  INFO [api-service,92e9013d25cca084,92e9013d25cca084,false] [10.242.22.52] 25453 --- [nio-8080-exec-1] c.e.s.WorkflowController : Received request
  • api-service is the service name
  • 92e9013d25cca084 is the trace ID; here it is also the span ID because this is the first span in the trace
  • false means that the log is not exportable to Zipkin

Getting the Sleuth trace ID

Getting the trace ID turned out to be very simple. We created a Spring service called Breadcrumb to return the formatted trace ID.

@Service
public class Breadcrumb {

  @Autowired
  private Tracer tracer;

  public String breadcrumbId() {
    return tracer.getCurrentSpan().traceIdString();
  }
}

Span#traceIdString() returns the trace ID in the same hexadecimal format used in the log messages.

The controller that handles the request to start the workflow injects this service and calls breadcrumb.getBreadcrumb() to get the current trace ID string.

Inserting the Sleuth trace ID

When a consumer requests status of a workflow they provide the breadcrumb ID to use, e.g.:

GET /v1/workflow/92e9013d25cca084

We want to set that value as the Sleuth trace ID.

Filter that injects the trace ID

The solution we chose is to create a Spring web filter that addes a Sleuth span as an attribute of the HTTP request so the Sleuth TraceFilter behaves as if the request is part of an existing Sleuth trace. Our filter:

  • is in the filter chain before TraceFilter
  • reads the breadcrumb ID from the request
  • constructs an appropriate Span object
  • adds the span as an attribute of the request with the correct key

When TraceFilter executes it finds the span in the request attribute and uses it as if the service had been called by another one in the same trace.

Here is the filter code, abridged for clarity, with explanatory comments:

// Load this filter before TraceFilter
@Order(TraceFilter.ORDER - 1)
public class InjectTraceFilter extends GenericFilterBean {

    // Only modify requests that match this pattern
    private static final Pattern REQUEST_PATTERN = Pattern.compile("^/v1/workflow/(?<crumbId>[0-9a-f]+)$");
    // Key of the request attribute that TraceFilter searches for
    private static final String TRACE_REQUEST_ATTR = TraceFilter.class.getName() + ".TRACE";
    // Used to construct the span name
    private static final String SPAN_NAME_BASE = "http:/v1/workflow/";

    private final Random random = new Random(System.currentTimeMillis());

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
                         final FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest httpRequest = (HttpServletRequest) request;
        final String breadcrumbId = extractBreadcrumbId(httpRequest);
        if (breadcrumbId != null) {
            // Set up a span with this breadcrumb ID as the trace ID
            httpRequest.setAttribute(TRACE_REQUEST_ATTR, spanForId(breadcrumbId));
            chain.doFilter(httpRequest, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    /**
     * Returns the breadcrumb ID if a GET request matches the pattern, otherwise null. 
     */
    private String extractBreadcrumbId(final HttpServletRequest httpRequest) {
        if ("GET".equals(httpRequest.getMethod())) {
            final Matcher matcher = REQUEST_PATTERN.matcher(httpRequest.getRequestURI());
            if (matcher.matches()) {
                return matcher.group("crumbId");
            }
        }
        return null;
    }

    /**
     * Constructs a span for the specified trace ID with a random span ID. 
     */
    private Span spanForId(final String traceId) {
        return Span.builder()
                .traceId(Span.hexToId(traceId))
                .spanId(random.nextLong())
                .exportable(false)
                .name(SPAN_NAME_BASE + traceId)
                .build();
    }
}

Configuration to use the filter

The filter is configured in the standard way:

@Configuration
public class InjectTraceFilterAutoConfigure {

    @Bean
    public InjectTraceFilter injectTraceFilter() {
        return new InjectTraceFilter();
    }

}

Showing tags on posts

|

After adding a Categories page I wanted to add tag-handling to the blog. I wanted to display the tags assigned to each post under the title.

Using the same principles as for categories and adapting some CSS from Codinfox, I have achieved that. Each tag name is a link to the relevant section of the Tags page.

Like categories, tags are specified for a post in the front matter, as for this post:

---
layout: post
title: Showing tags on posts
categories: tech
tags: blog
---

Adding a categories page to this blog

|

I succeeded in adding a Categories page to this blog.

Jekyll supports specification of variables category, categories and tags in the front matter of blog posts but it is left as an Exercise for the Reader™ to do anything with them. The Lanyon theme does not itself include page with category or tag lists (but of course is infinitely expandable).

So I created a contents page by adapting this technique with my own amateur CSS applied. The page is contents.html in the root directory of the site and has front matter:

---
layout: page
title: Categories
---

The Lanyon sidebar automatically creates links to any pages with layout: page in the front matter so the Categories page is easily found.