-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
563 lines (281 loc) · 119 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Thomas Piart - tpî.eu - Personal site</title>
<link href="https://tpî.eu/atom.xml" rel="self"/>
<link href="https://tpî.eu/"/>
<updated>2024-06-09T00:00:00.000Z</updated>
<id>https://tpî.eu/</id>
<author>
<name>Thomas Piart</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Structured Logging in ASP.Net with ELK - thinking beyond the basics</title>
<link href="https://tpî.eu/2024/06/09/Structured-logging-thinking-beyond-the-basics/"/>
<id>https://tpî.eu/2024/06/09/Structured-logging-thinking-beyond-the-basics/</id>
<published>2024-06-09T00:00:00.000Z</published>
<updated>2024-06-09T00:00:00.000Z</updated>
<content type="html"><![CDATA[<h2>Why are we still talking about it?</h2><p>This article is on how to go beyond structured logging, especially in the context of Logging in Json when the Json is ingested by Elastic Search via Filebeat (or equivalent)</p><p>There are constraints in this case that needs to be taken into account if you want to optimize your logging output</p><h2>What you are doing</h2><p>So you are logging for Elastic Search.</p><p>Your logs are going to be scrapped from the console and sent to Elastic and you are going to be able to display them nicely in Kibana.</p><p>Using Microsoft.Extensions.Logging (<code>ILogger<T></code>) and JsonConsoleFormatter, you should have code logging like that:</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// This is set via injection</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">readonly</span> ILogger<MyClass> _logger;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*...*/</span></span><br><span class="line"><span class="built_in">int</span> countMessages = <span class="number">5000</span>;</span><br><span class="line">_logger.LogInformation(<span class="string">"Starting Process"</span>);</span><br><span class="line"><span class="comment">// This is a scope declared high up in the process</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> globalLogScope = _logger.BeginScope(<span class="keyword">new</span> List<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> { <span class="keyword">new</span>(<span class="string">"TransactionId"</span>, <span class="string">"ABCDE"</span>) });</span><br><span class="line"><span class="comment">// This is a more local scope applied to only part of the process</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> logScope = _logger.BeginScope(<span class="keyword">new</span> List<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> { <span class="keyword">new</span>(<span class="string">"RecordId"</span>, <span class="number">1234</span>) });</span><br><span class="line">_logger.LogWarning(<span class="string">"Too many messages: {Count}"</span>, countMessages);</span><br></pre></td></tr></table></figure><p>And this will produce the following result:</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"EventId"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Information"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"MyApplication.Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"State"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"EventId"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Warning"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"MyApplication.Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Too many messages: 5000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"State"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Too many messages: 5000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Count"</span><span class="punctuation">:</span> <span class="number">5000</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Too many messages: {Count}"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Scopes"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"System.Collections.Generic.List\u00601[System.Collections.Generic.KeyValuePair\u00602[System.String,System.Object]]"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"TransactionId"</span><span class="punctuation">:</span> <span class="string">"ABCDE"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"System.Collections.Generic.List\u00601[System.Collections.Generic.KeyValuePair\u00602[System.String,System.Object]]"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"RecordId"</span><span class="punctuation">:</span> <span class="number">1234</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>I have indented those two Jsons, but by default (and this is good for parser), each json will be serialized on a single line. The initial size is 718 bytes</p><h2>Can we improve ?</h2><p>Right… There are plenty of issues with this output. Let’s address them :)</p><p>This is <strong>super verbose and redundant</strong> and this is an issue because we are logging to the console.<br>By default, if a message is longer than ~16k, it will be split over multiple lines, and FileBeat will have a hard time parsing it.</p><p>Note that the max size of the console line can be different on your system, and you (or your DevOps) can change it but the longer it is, the longer your message can be, and that might be an issue for Filebeat (or the equivalent on your system) in terms of performances, throughput, …</p><p>So our objective will be to reduce verbosity and redundancy when possible</p><ul><li>There are useless properties<ul><li>Default values could be skipped, like EventId = 0. I rarely use EventId, but when I use it, it’s nice to be able to filter by this value in Kibana</li></ul></li><li>There are redundant properties:<ul><li>Message is duplicated in Message and in State.Message, even when there is no placeholder</li><li>It is nice to have access to State.{OriginalFormat} because I can easily filter on this type of message in Kibana</li></ul></li></ul><h2>How do we do that ?</h2><p>Let’s take the json console log formatter from Microsoft : <a href="https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs">JsonConsoleFormatter</a> (and <a href="https://github.com/dotnet/runtime/blob/02ddff3430d976a7cb3a785b1ec7e33e80796e71/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs">the specific commit at the time of writing</a>)</p><p>And adapt it to our needs.</p><p>If we copy this class in our project, we’ll also need to create a CompactJsonConsoleFormatterOptions (the equivalent of <a href="https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatterOptions.cs">JsonConsoleFormatterOptions</a> )</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">CompactJsonConsoleFormatterOptions</span> : <span class="title">ConsoleFormatterOptions</span></span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">CompactJsonConsoleFormatterOptions</span>()</span> { }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> JsonWriterOptions JsonWriterOptions { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>And the <a href="https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs">PooledByteBufferWriter</a> (internal ATTOW)</p><p>I’m going to skip most default values:</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// before</span></span><br><span class="line">writer.WriteNumber(<span class="keyword">nameof</span>(logEntry.EventId), eventId);</span><br><span class="line"></span><br><span class="line"><span class="comment">// after</span></span><br><span class="line"><span class="keyword">if</span> (eventId > <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"> writer.WriteNumber(<span class="keyword">nameof</span>(logEntry.EventId), eventId);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Same for all the items (scope or state):</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// before</span></span><br><span class="line">writer.WriteBoolean(key, boolValue);</span><br><span class="line"></span><br><span class="line"><span class="comment">// after</span></span><br><span class="line"><span class="keyword">if</span> (boolValue)</span><br><span class="line">{</span><br><span class="line"> writer.WriteBoolean(key, boolValue);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>I’m also merging scope and state, and renaming them to something shorter (<code>Sc</code>):</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// before</span></span><br><span class="line"><span class="keyword">if</span> (logEntry.State != <span class="literal">null</span>)</span><br><span class="line">{</span><br><span class="line"> writer.WriteStartObject(<span class="keyword">nameof</span>(logEntry.State));</span><br><span class="line"> writer.WriteString(<span class="string">"Message"</span>, logEntry.State.ToString());</span><br><span class="line"> <span class="keyword">if</span> (logEntry.State <span class="keyword">is</span> IReadOnlyCollection<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> stateProperties)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">foreach</span> (KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>> item <span class="keyword">in</span> stateProperties)</span><br><span class="line"> {</span><br><span class="line"> WriteItem(writer, item);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> writer.WriteEndObject();</span><br><span class="line">}</span><br><span class="line">WriteScopeInformation(writer, scopeProvider);</span><br><span class="line"></span><br><span class="line"><span class="comment">// after</span></span><br><span class="line"> writer.WriteStartObject(<span class="string">"Sc"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (logEntry.State != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> writer.WriteString(<span class="string">"Message"</span>, logEntry.State.ToString());</span><br><span class="line"> <span class="keyword">if</span> (logEntry.State <span class="keyword">is</span> IReadOnlyCollection<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> stateProperties)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">foreach</span> (KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>> item <span class="keyword">in</span> stateProperties)</span><br><span class="line"> {</span><br><span class="line"> WriteItem(writer, item);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> WriteScopeInformation(writer, scopeProvider);</span><br><span class="line"> </span><br><span class="line"> writer.WriteEndObject();</span><br></pre></td></tr></table></figure><p>The <em>small</em> issue with this code is that I’m always creating the “Sc” Object in Json, whether of not there is a scope or state in this log entry.</p><p>Now let’s flatten Scopes:</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// before</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">WriteScopeInformation</span>(<span class="params">Utf8JsonWriter writer, IExternalScopeProvider? scopeProvider</span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (FormatterOptions.IncludeScopes && scopeProvider != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> writer.WriteStartArray(<span class="string">"Scopes"</span>);</span><br><span class="line"> scopeProvider.ForEachScope((scope, state) =></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (scope <span class="keyword">is</span> IEnumerable<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> scopeItems)</span><br><span class="line"> {</span><br><span class="line"> state.WriteStartObject();</span><br><span class="line"> state.WriteString(<span class="string">"Message"</span>, scope.ToString());</span><br><span class="line"> <span class="keyword">foreach</span> (KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>> item <span class="keyword">in</span> scopeItems)</span><br><span class="line"> {</span><br><span class="line"> WriteItem(state, item);</span><br><span class="line"> }</span><br><span class="line"> state.WriteEndObject();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> state.WriteStringValue(ToInvariantString(scope));</span><br><span class="line"> }</span><br><span class="line"> }, writer);</span><br><span class="line"> writer.WriteEndArray();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// after:</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">WriteScopeInformation</span>(<span class="params">Utf8JsonWriter writer, IExternalScopeProvider? scopeProvider</span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (FormatterOptions.IncludeScopes && scopeProvider != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> scopeProvider.ForEachScope((scope, state) =></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (scope <span class="keyword">is</span> IEnumerable<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> scopeItems)</span><br><span class="line"> {</span><br><span class="line"> state.WriteString(<span class="string">"Message"</span>, scope.ToString());</span><br><span class="line"> <span class="keyword">foreach</span> (KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>> item <span class="keyword">in</span> scopeItems)</span><br><span class="line"> {</span><br><span class="line"> WriteItem(state, item);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> state.WriteStringValue(ToInvariantString(scope));</span><br><span class="line"> }</span><br><span class="line"> }, writer);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>The new json looks like this:</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Information"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"MyApplication.Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sc"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Warning"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"MyApplication.Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Too many messages: 5000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sc"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Too many messages: 5000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Count"</span><span class="punctuation">:</span> <span class="number">5000</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Too many messages: {Count}"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"System.Collections.Generic.List\u00601[System.Collections.Generic.KeyValuePair\u00602[System.String,System.Object]]"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"TransactionId"</span><span class="punctuation">:</span> <span class="string">"ABCDE"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"System.Collections.Generic.List\u00601[System.Collections.Generic.KeyValuePair\u00602[System.String,System.Object]]"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"RecordId"</span><span class="punctuation">:</span> <span class="number">1234</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>As you can see, we have invalid json with a duplicate property: <code>Message</code>.<br>The good news is that the state message is redundant with the field message so we can remove it</p><p>Also, in the case of scope messages, it’s a <code>.ToString()</code> of the scope object.<br>In our case, it’s just a <code>List<T></code>, so it is useless.<br>In the case of Microsoft Scopes (request scopes), the object used is a bit more interesting, it is an internal object <a href="https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactoryScopeProvider.cs#L186">ActivityLogScope</a> overloading the <code>ToString()</code> function.<br>However this implementation is just repeating the same exact information that we have as properties later in the message, so we can remove this Message also.</p><p>Here is the new json:</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Information"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"MyApplication.Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sc"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Warning"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"MyApplication.Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Too many messages: 5000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sc"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Count"</span><span class="punctuation">:</span> <span class="number">5000</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Too many messages: {Count}"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"TransactionId"</span><span class="punctuation">:</span> <span class="string">"ABCDE"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"RecordId"</span><span class="punctuation">:</span> <span class="number">1234</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>We can go further :)</p><p>The Category property represents the namespace of the class emitting the log.<br>What is expected is that most of the logs will come from our own namespace (in my case <code>MyApplication.</code>) so we could replace this with something shorter (a <code>-</code>).<br>Also, when there is no state (no placeholder in our message), the state property <code>{OriginalFormat}</code> contains the same information as our Message, so we can drop it.</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Information"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"-Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Starting Process"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sc"</span><span class="punctuation">:</span> <span class="punctuation">{</span><span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Warning"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"-Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Too many messages: 5000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sc"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Count"</span><span class="punctuation">:</span> <span class="number">5000</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Too many messages: {Count}"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"TransactionId"</span><span class="punctuation">:</span> <span class="string">"ABCDE"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"RecordId"</span><span class="punctuation">:</span> <span class="number">1234</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>We are now at 318 bytes, less than half of the initial size</p><p>Also, if we log a large message, it won’t be duplicated in the json if it contains no placeholder.<br>So if you are dumping potentially large payload in your message and you make sure you are not using placeholders, then you can cut the size of your logs in two</p><p>Is if all great, but we have just created an issue which was not present before:</p><p>If two scopes share the same property, they could collide! Same for a scope and a state sharing the same property</p><p>Ex:</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> globalLogScope = _logger.BeginScope(<span class="keyword">new</span> List<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> { <span class="keyword">new</span>(<span class="string">"Collide"</span>, <span class="string">"ABCDE"</span>), });</span><br><span class="line"><span class="comment">// This is a more local scope applied to only part of the process</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> logScope = _logger.BeginScope(<span class="keyword">new</span> List<KeyValuePair<<span class="built_in">string</span>, <span class="built_in">object</span>>> { <span class="keyword">new</span>(<span class="string">"Collide"</span>, <span class="number">1234</span>), });</span><br><span class="line">_logger.LogWarning(<span class="string">"Too many messages: {Collide}"</span>, countMessages);</span><br></pre></td></tr></table></figure><p>Will produce:</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"LogLevel"</span><span class="punctuation">:</span> <span class="string">"Warning"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Category"</span><span class="punctuation">:</span> <span class="string">"-Controllers.MyController"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Message"</span><span class="punctuation">:</span> <span class="string">"Too many messages: 5000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sc"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Collide"</span><span class="punctuation">:</span> <span class="number">5000</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"{OriginalFormat}"</span><span class="punctuation">:</span> <span class="string">"Too many messages: {Collide}"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Collide"</span><span class="punctuation">:</span> <span class="string">"ABCDE"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Collide"</span><span class="punctuation">:</span> <span class="number">1234</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>So let’s fix this by using a small HashSet to detect collisions and resolve them with a suffix number</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> suffixString = <span class="string">""</span>;</span><br><span class="line"><span class="keyword">var</span> suffixNumber = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span>(keys.Contains(item.Key + suffixString) && suffixNumber < <span class="number">10</span>){</span><br><span class="line"> suffixNumber++;</span><br><span class="line"> suffixString = suffixNumber.ToString();</span><br><span class="line">}</span><br><span class="line">keys.TryAdd(item.Key + suffixString);</span><br><span class="line">writer.WriteString(item.Key + suffixString, item.Value.ToString());</span><br></pre></td></tr></table></figure><p>This code is not bullet proof, but I have rarely seen more than one collision.</p><p>Et voila, a much optimized Json Console formatter.</p>]]></content>
<summary type="html"><h2>Why are we still talking about it?</h2>
<p>This article is on how to go beyond structured logging, especially in the context of Logging </summary>
<category term="csharp" scheme="https://tpî.eu/tags/csharp/"/>
<category term="aspnetcore" scheme="https://tpî.eu/tags/aspnetcore/"/>
</entry>
<entry>
<title>Deploy hexo on 000webhost</title>
<link href="https://tpî.eu/2021/03/19/deploy-hexo-on-000webhost/"/>
<id>https://tpî.eu/2021/03/19/deploy-hexo-on-000webhost/</id>
<published>2021-03-19T00:00:00.000Z</published>
<updated>2021-03-19T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>Deploying a Hexo site on <strong>000webhost</strong> is super easy!</p><p>Let’s remind some basics:</p><ul><li><a href="https://hexo.io">Hexo</a> is a static site generator. Meaning that you can generate a pure HTML site that does not rely on a backend server (PHP, nodejs, …)</li><li><a href="https://www.000webhost.com/">000webhost</a> is a very classic web hosting provider, it allow you to host a static web sites as well as a more traditional dynamic site with PHP. They try very hard to make you upgrade to their paid offer.</li></ul><h2>Pre requisites</h2><p>You’ll need your Hexo site built.</p><p>If you don’t have one already, you can <a href="https://hexo.io/docs/#Installation">follow this doc to create one</a> or even simpler, <a href="https://github.com/hexojs/hexo-starter">fork the hexo-starter repository</a>.</p><p>Then run the command <code>hexo g</code> or <code>npx hexo g</code> (if you don’t have hexo installed globally on your machine). If your configuration is standard, you should find your site in the folder <code>public</code>.</p><h2>First Deploy</h2><p>Once you are ready, go to <a href="https://www.000webhost.com/free-website-sign-up">https://www.000webhost.com/free-website-sign-up</a></p><p>I chose to login with my google account, but you can choose to create a local account</p><p>You’ll then be prompted to create a site with their site builder tool. You can skip that part :)</p><p>Just create a new website with a new random password</p><p>You’ll be able to use this password to upload your site via FTP (the alternative it to use their file manager, which isn’t usable to upload a site with multiple sub folders)</p><p>Via you ftp client (Filezilla for instance), login to <code>files.000webhost.com</code>. The username is the website name you chose above, same for the password.</p><p>Once your site is uploaded, you can also tweak the <code>.htaccess</code> file to handle your 404 page (if you have one). Here is the minimal content you could use:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">ErrorDocument 404 /assets/404.html</span><br></pre></td></tr></table></figure><p>If you want to automate ftp upload from github, you can probably use something like what I did with ftp upload to OVH:<br><a href="https://github.com/tomap/tpi2.eu/blob/master/.github/workflows/nodejs.yml#L53">https://github.com/tomap/tpi2.eu/blob/master/.github/workflows/nodejs.yml#L53</a><br>or see my previous post on the subject: <a href="/2017/04/24/Travis-setup/">Travis setup</a></p><h2>Custom Domain</h2><p>You can configure a domain to point to your site.</p><p>If your site url is <a href="http://tpi2-eu.000webhostapp.com">tpi2-eu.000webhostapp.com</a> like mine is, you can add a CNAME like this one:</p><p>CNAME: <a href="http://000webhost.xn--tp-rja.eu">000webhost.tpî.eu</a> -> <a href="http://tpi2-eu.000webhostapp.com">tpi2-eu.000webhostapp.com</a></p><p>For me, the result is here:</p><ul><li><a href="https://tpi2-eu.000webhostapp.com/">https://tpi2-eu.000webhostapp.com/</a></li><li><a href="http://000webhost.xn--tp-rja.eu/">http://000webhost.tpî.eu/</a></li></ul><h2>Conclusion</h2><p>You should not use them. There are much bette offer out there for free hosting.</p><ol><li>They add an ugly banner at the bottom of each pages (after thinking a bit about it, you should probably be able to kill it using CSP, and disallowing inline JS, but they might kick you out). I made a <a href="https://github.com/ripienaar/free-for-dev/pull/1749">PR about that on free-for-dev</a></li><li>They don’t provide SSL certificate if you use your own custom domain</li><li>They got breached in 2015, with clear passwords: <a href="https://haveibeenpwned.com/PwnedWebsites#000webhost">https://haveibeenpwned.com/PwnedWebsites#000webhost</a></li></ol>]]></content>
<summary type="html"><p>Deploying a Hexo site on <strong>000webhost</strong> is super easy!</p>
<p>Let’s remind some basics:</p>
<ul>
<li><a href="https://hexo.i</summary>
<category term="hexo" scheme="https://tpî.eu/tags/hexo/"/>
<category term="hosting" scheme="https://tpî.eu/tags/hosting/"/>
<category term="000webhost" scheme="https://tpî.eu/tags/000webhost/"/>
</entry>
<entry>
<title>Deploy hexo on Vercel</title>
<link href="https://tpî.eu/2020/12/30/deploy-hexo-on-vercel/"/>
<id>https://tpî.eu/2020/12/30/deploy-hexo-on-vercel/</id>
<published>2020-12-30T00:00:00.000Z</published>
<updated>2020-12-30T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>Deploying a Hexo site on <strong>Vercel</strong> is super easy!</p><p>Let’s remind some basics:</p><ul><li><a href="https://hexo.io">Hexo</a> is a static site generator. Meaning that you can generate a pure HTML site that does not rely on a backend server (PHP, nodejs, …)</li><li><a href="https://vercel.com/">Vercel</a> is a bit like Netlify, it allows you to host your static site, and offers you features to make it more dynamic (authentication, lambda functions)</li></ul><p>At Vercel, they made a great job at making the onboarding easy and complete.</p><h2>Pre requisites</h2><p>Your Hexo site sources must be hosted on GitHub (also works with GitLab and Bitbucket).</p><p>If you don’t have one already, you can <a href="https://hexo.io/docs/#Installation">follow this doc to create one</a> or even simpler, <a href="https://github.com/hexojs/hexo-starter">fork the hexo-starter repository</a>.</p><h2>First Deploy</h2><p>Once you are ready, go to <a href="https://vercel.com">https://vercel.com</a></p><p>Choose the GitHub login (button “Continue with GitHub”)</p><p>Then click on “Continue” below “Import Git Repository”.</p><p>Paste your git repository url: <code>https://github.com/<username>/hexo-starter</code></p><p>Vercel will ask you if it’s your repository or not. Say Yes.</p><p>It will redirect you to GitHub Authorization screen, where you need to allow Vercel to have access to your repository.</p><p>Once you are done, Vercel will deploy your site to their domain: <a href="https://my-site.vercel.app">https://my-site.vercel.app</a></p><h2>Custom Domain</h2><p>You can easily configure a domain to point to your Vercel deployment via the Menu: <em>My Project</em> > Settings > Domains where they ask you to provide the domain name you want to define, and then you have to add a <strong>CNAME</strong> to your DNS</p><p>And voila :)</p><p>For me, the result is here:</p><ul><li><a href="https://tpi2-eu.vercel.app/">https://tpi2-eu.vercel.app/</a></li><li><a href="https://vercel.xn--tp-rja.eu/">https://vercel.tpî.eu/</a></li></ul>]]></content>
<summary type="html"><p>Deploying a Hexo site on <strong>Vercel</strong> is super easy!</p>
<p>Let’s remind some basics:</p>
<ul>
<li><a href="https://hexo.io">H</summary>
<category term="hexo" scheme="https://tpî.eu/tags/hexo/"/>
<category term="hosting" scheme="https://tpî.eu/tags/hosting/"/>
<category term="vercel" scheme="https://tpî.eu/tags/vercel/"/>
</entry>
<entry>
<title>Flickr tag improved</title>
<link href="https://tpî.eu/2020/10/10/flickr-improved/"/>
<id>https://tpî.eu/2020/10/10/flickr-improved/</id>
<published>2020-10-10T00:00:00.000Z</published>
<updated>2020-10-10T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>I spent some time improving the <strong>hexo-tag-flickr</strong> plugin that displays images from flickr on this site.</p><p>Here is what I did:</p><h2>Live Urls</h2><p>I switched from the <code>farm*.staticflickr.com</code> urls to <em><a href="http://live.staticflickr.com">live.staticflickr.com</a></em></p><p>By doing so, I spared myself a call to <a href="https://www.flickr.com/services/api/explore/flickr.photos.getInfo">flickr.photos.getInfo</a> API for each images.</p><p>Because all that is needed is a call to <a href="https://www.flickr.com/services/api/explore/flickr.photos.getSizes">flickr.photos.getSizes</a></p><h2>Srcset</h2><p>I included the <em>srcset</em> attribute. This attribute is useful to provide multiple size of the same image and let the browser decide which is better depending on the context. For example, on mobile, a small image might be enough, and on a desktop a bigger image might be needed.</p><p>A demonstration of this is below:</p><a href='//flic.kr/p/2jQR5bz/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/50435880733_7f240f8af0_o.jpg" width="20000" srcset="//live.staticflickr.com/65535/50435880733_3b6d5f6a4b_s.jpg 75w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_q.jpg 150w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_t.jpg 100w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_m.jpg 240w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_n.jpg 320w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_w.jpg 400w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b.jpg 500w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_z.jpg 640w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_c.jpg 800w, //live.staticflickr.com/65535/50435880733_3b6d5f6a4b_b.jpg 1024w, //live.staticflickr.com/65535/50435880733_cc4ff56798_h.jpg 1600w, //live.staticflickr.com/65535/50435880733_a2be39edd2_k.jpg 2048w, //live.staticflickr.com/65535/50435880733_116168d5e8_f.jpg 4096w, //live.staticflickr.com/65535/50435880733_7f240f8af0_o.jpg 20000w" class=""></a><figure class="highlight md"><table><tr><td class="code"><pre><span class="line">{% flickr 50435880733 o %}</span><br></pre></td></tr></table></figure><p>I’ve embedded a <strong>20000px</strong> large image. And you have only downloaded a 2048px image (or smaller) because your browser determined it was enough. See here for more explanation: <a href="https://flaviocopes.com/html-responsive-images-srcset/">https://flaviocopes.com/html-responsive-images-srcset/</a></p><p>Unless you are still using IE, in which case, … I’m sorry (see <a href="https://caniuse.com/srcset">https://caniuse.com/srcset</a> for support)</p><h2>The rest</h2><p>I also did a bunch of other fixes. See <a href="https://github.com/tomap/hexo-tag-flickr/commit/f8d58bb6e44307ba5d03ba04c81d22cac640ded3">https://github.com/tomap/hexo-tag-flickr/commit/f8d58bb6e44307ba5d03ba04c81d22cac640ded3</a> I let you check them out.</p><p>EDIT 12/10: It seems Feedly crashes when displaying a post with such a large image…</p>]]></content>
<summary type="html"><p>I spent some time improving the <strong>hexo-tag-flickr</strong> plugin that displays images from flickr on this site.</p>
<p>Here is wha</summary>
<category term="flickr" scheme="https://tpî.eu/tags/flickr/"/>
<category term="hexo" scheme="https://tpî.eu/tags/hexo/"/>
</entry>
<entry>
<title>IDN and browser support 2</title>
<link href="https://tpî.eu/2019/06/21/IDN-and-browser-support-2/"/>
<id>https://tpî.eu/2019/06/21/IDN-and-browser-support-2/</id>
<published>2019-06-21T00:00:00.000Z</published>
<updated>2019-06-21T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>This post is about IDN (International Domain Names) and browser support in 2019, 5 years after the previous one. And hopefully, I’ll publish this one faster than the previous one.</p><h2>Android</h2><p>In 2019, Android has a lot of browser, but I only looked at a few of them:</p><h3>Chrome</h3><p>Perfect</p><a href='//flic.kr/p/23P6UAn/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/7870/39914433873_6701d6c2ee.jpg" width="281" srcset="//live.staticflickr.com/7870/39914433873_6701d6c2ee_s.jpg 75w, //live.staticflickr.com/7870/39914433873_6701d6c2ee_q.jpg 150w, //live.staticflickr.com/7870/39914433873_6701d6c2ee_t.jpg 56w, //live.staticflickr.com/7870/39914433873_6701d6c2ee_m.jpg 135w, //live.staticflickr.com/7870/39914433873_6701d6c2ee_n.jpg 180w, //live.staticflickr.com/7870/39914433873_6701d6c2ee_w.jpg 225w, //live.staticflickr.com/7870/39914433873_6701d6c2ee.jpg 281w" class=""></a><h3>Firefox</h3><p>Perfect</p><a href='//flic.kr/p/23P6UA2/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/7811/39914433853_c2fdbcc26b.jpg" width="281" srcset="//live.staticflickr.com/7811/39914433853_c2fdbcc26b_s.jpg 75w, //live.staticflickr.com/7811/39914433853_c2fdbcc26b_q.jpg 150w, //live.staticflickr.com/7811/39914433853_c2fdbcc26b_t.jpg 56w, //live.staticflickr.com/7811/39914433853_c2fdbcc26b_m.jpg 135w, //live.staticflickr.com/7811/39914433853_c2fdbcc26b_n.jpg 180w, //live.staticflickr.com/7811/39914433853_c2fdbcc26b_w.jpg 225w, //live.staticflickr.com/7811/39914433853_c2fdbcc26b.jpg 281w" class=""></a><h3>Edge</h3><p>Perfect</p><a href='//flic.kr/p/23P6UAH/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/4821/39914433893_4659582ca0.jpg" width="281" srcset="//live.staticflickr.com/4821/39914433893_4659582ca0_s.jpg 75w, //live.staticflickr.com/4821/39914433893_4659582ca0_q.jpg 150w, //live.staticflickr.com/4821/39914433893_4659582ca0_t.jpg 56w, //live.staticflickr.com/4821/39914433893_4659582ca0_m.jpg 135w, //live.staticflickr.com/4821/39914433893_4659582ca0_n.jpg 180w, //live.staticflickr.com/4821/39914433893_4659582ca0_w.jpg 225w, //live.staticflickr.com/4821/39914433893_4659582ca0.jpg 281w" class=""></a><h3>DuckDuckGo</h3><p>Not properly displayed (Yes, they have a browser)</p><a href='//flic.kr/p/23P6UAT/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/7872/39914433903_e942069b9a.jpg" width="281" srcset="//live.staticflickr.com/7872/39914433903_e942069b9a_s.jpg 75w, //live.staticflickr.com/7872/39914433903_e942069b9a_q.jpg 150w, //live.staticflickr.com/7872/39914433903_e942069b9a_t.jpg 56w, //live.staticflickr.com/7872/39914433903_e942069b9a_m.jpg 135w, //live.staticflickr.com/7872/39914433903_e942069b9a_n.jpg 180w, //live.staticflickr.com/7872/39914433903_e942069b9a_w.jpg 225w, //live.staticflickr.com/7872/39914433903_e942069b9a.jpg 281w" class=""></a><p>It’s even worse, if you type only ‘<a href="http://xn--tp-rja.eu">tpî.eu</a>’, you get redirected to the search engine, not the famous site. You need to type the full https://<br>But I get it, for a privacy oriented browser, it’s probably more important to prevent phishing than to play nice with IDN</p><h2>iOS</h2><p>Tests were done on an iPad.</p><h3>Safari</h3><p>Perfect</p><a href='//flic.kr/p/23P6Sqa/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/4902/39914426553_2d3bd71a71_z.jpg" width="480" srcset="//live.staticflickr.com/4902/39914426553_2d3bd71a71_s.jpg 75w, //live.staticflickr.com/4902/39914426553_2d3bd71a71_q.jpg 150w, //live.staticflickr.com/4902/39914426553_2d3bd71a71_t.jpg 75w, //live.staticflickr.com/4902/39914426553_2d3bd71a71_m.jpg 180w, //live.staticflickr.com/4902/39914426553_2d3bd71a71_n.jpg 240w, //live.staticflickr.com/4902/39914426553_2d3bd71a71_w.jpg 300w, //live.staticflickr.com/4902/39914426553_2d3bd71a71.jpg 375w, //live.staticflickr.com/4902/39914426553_2d3bd71a71_z.jpg 480w" class=""></a><h3>Chrome</h3><p>Perfect</p><a href='//flic.kr/p/23P6SqR/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/4871/39914426593_afe40e311d_z.jpg" width="480" srcset="//live.staticflickr.com/4871/39914426593_afe40e311d_s.jpg 75w, //live.staticflickr.com/4871/39914426593_afe40e311d_q.jpg 150w, //live.staticflickr.com/4871/39914426593_afe40e311d_t.jpg 75w, //live.staticflickr.com/4871/39914426593_afe40e311d_m.jpg 180w, //live.staticflickr.com/4871/39914426593_afe40e311d_n.jpg 240w, //live.staticflickr.com/4871/39914426593_afe40e311d_w.jpg 300w, //live.staticflickr.com/4871/39914426593_afe40e311d.jpg 375w, //live.staticflickr.com/4871/39914426593_afe40e311d_z.jpg 480w" class=""></a><h3>Firefox</h3><p>Not properly displayed</p><a href='//flic.kr/p/23P6Sqv/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/4828/39914426573_44a5ed2556_z.jpg" width="480" srcset="//live.staticflickr.com/4828/39914426573_44a5ed2556_s.jpg 75w, //live.staticflickr.com/4828/39914426573_44a5ed2556_q.jpg 150w, //live.staticflickr.com/4828/39914426573_44a5ed2556_t.jpg 75w, //live.staticflickr.com/4828/39914426573_44a5ed2556_m.jpg 180w, //live.staticflickr.com/4828/39914426573_44a5ed2556_n.jpg 240w, //live.staticflickr.com/4828/39914426573_44a5ed2556_w.jpg 300w, //live.staticflickr.com/4828/39914426573_44a5ed2556.jpg 375w, //live.staticflickr.com/4828/39914426573_44a5ed2556_z.jpg 480w" class=""></a><p>It’s working on Android but not on iOS… Why?</p><h3>Edge</h3><p>Not properly displayed</p><a href='//flic.kr/p/23P6Sqk/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/4889/39914426563_5b128cfa92_z.jpg" width="480" srcset="//live.staticflickr.com/4889/39914426563_5b128cfa92_s.jpg 75w, //live.staticflickr.com/4889/39914426563_5b128cfa92_q.jpg 150w, //live.staticflickr.com/4889/39914426563_5b128cfa92_t.jpg 75w, //live.staticflickr.com/4889/39914426563_5b128cfa92_m.jpg 180w, //live.staticflickr.com/4889/39914426563_5b128cfa92_n.jpg 240w, //live.staticflickr.com/4889/39914426563_5b128cfa92_w.jpg 300w, //live.staticflickr.com/4889/39914426563_5b128cfa92.jpg 375w, //live.staticflickr.com/4889/39914426563_5b128cfa92_z.jpg 480w" class=""></a><p>It’s working on Android but not on iOS… Why?</p><h3>DuckDuckGo</h3><p>Not properly displayed</p><a href='//flic.kr/p/ShrdrA/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/7915/33003821988_ce03a81d2c_z.jpg" width="480" srcset="//live.staticflickr.com/7915/33003821988_ce03a81d2c_s.jpg 75w, //live.staticflickr.com/7915/33003821988_ce03a81d2c_q.jpg 150w, //live.staticflickr.com/7915/33003821988_ce03a81d2c_t.jpg 75w, //live.staticflickr.com/7915/33003821988_ce03a81d2c_m.jpg 180w, //live.staticflickr.com/7915/33003821988_ce03a81d2c_n.jpg 240w, //live.staticflickr.com/7915/33003821988_ce03a81d2c_w.jpg 300w, //live.staticflickr.com/7915/33003821988_ce03a81d2c.jpg 375w, //live.staticflickr.com/7915/33003821988_ce03a81d2c_z.jpg 480w" class=""></a><h2>PC</h2><p>Windows 10, fully patched, all is perfect</p><h3>Chrome</h3><p>Perfect</p><a href='//flic.kr/p/2ghwR2o/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/48101126276_bafc95c763_c.jpg" width="711" srcset="//live.staticflickr.com/65535/48101126276_bafc95c763_s.jpg 75w, //live.staticflickr.com/65535/48101126276_bafc95c763_q.jpg 150w, //live.staticflickr.com/65535/48101126276_bafc95c763_t.jpg 89w, //live.staticflickr.com/65535/48101126276_bafc95c763_m.jpg 213w, //live.staticflickr.com/65535/48101126276_bafc95c763_n.jpg 284w, //live.staticflickr.com/65535/48101126276_bafc95c763_w.jpg 356w, //live.staticflickr.com/65535/48101126276_bafc95c763.jpg 444w, //live.staticflickr.com/65535/48101126276_bafc95c763_z.jpg 569w, //live.staticflickr.com/65535/48101126276_bafc95c763_c.jpg 711w" class=""></a><h3>Firefox</h3><p>Perfect</p><a href='//flic.kr/p/2ghx5QY/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/48101172768_29abfabf76_c.jpg" width="711" srcset="//live.staticflickr.com/65535/48101172768_29abfabf76_s.jpg 75w, //live.staticflickr.com/65535/48101172768_29abfabf76_q.jpg 150w, //live.staticflickr.com/65535/48101172768_29abfabf76_t.jpg 89w, //live.staticflickr.com/65535/48101172768_29abfabf76_m.jpg 213w, //live.staticflickr.com/65535/48101172768_29abfabf76_n.jpg 284w, //live.staticflickr.com/65535/48101172768_29abfabf76_w.jpg 356w, //live.staticflickr.com/65535/48101172768_29abfabf76.jpg 444w, //live.staticflickr.com/65535/48101172768_29abfabf76_z.jpg 569w, //live.staticflickr.com/65535/48101172768_29abfabf76_c.jpg 711w" class=""></a><h3>Edge</h3><p>Perfect</p><a href='//flic.kr/p/2ghwR3L/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/48101126356_b24c2f4635_c.jpg" width="711" srcset="//live.staticflickr.com/65535/48101126356_b24c2f4635_s.jpg 75w, //live.staticflickr.com/65535/48101126356_b24c2f4635_q.jpg 150w, //live.staticflickr.com/65535/48101126356_b24c2f4635_t.jpg 89w, //live.staticflickr.com/65535/48101126356_b24c2f4635_m.jpg 213w, //live.staticflickr.com/65535/48101126356_b24c2f4635_n.jpg 284w, //live.staticflickr.com/65535/48101126356_b24c2f4635_w.jpg 356w, //live.staticflickr.com/65535/48101126356_b24c2f4635.jpg 444w, //live.staticflickr.com/65535/48101126356_b24c2f4635_z.jpg 569w, //live.staticflickr.com/65535/48101126356_b24c2f4635_c.jpg 711w" class=""></a><h3>Internet Explorer</h3><p>Perfect</p><a href='//flic.kr/p/2ghwR2P/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/48101126301_ff2602061d_c.jpg" width="711" srcset="//live.staticflickr.com/65535/48101126301_ff2602061d_s.jpg 75w, //live.staticflickr.com/65535/48101126301_ff2602061d_q.jpg 150w, //live.staticflickr.com/65535/48101126301_ff2602061d_t.jpg 89w, //live.staticflickr.com/65535/48101126301_ff2602061d_m.jpg 213w, //live.staticflickr.com/65535/48101126301_ff2602061d_n.jpg 284w, //live.staticflickr.com/65535/48101126301_ff2602061d_w.jpg 356w, //live.staticflickr.com/65535/48101126301_ff2602061d.jpg 444w, //live.staticflickr.com/65535/48101126301_ff2602061d_z.jpg 569w, //live.staticflickr.com/65535/48101126301_ff2602061d_c.jpg 711w" class=""></a><h2>Mac</h2><p>All browsers work fine</p><h3>Safari</h3><p>Perfect</p><a href='//flic.kr/p/2gibAX5/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/48108493186_4a57522bdf_c.jpg" width="800" srcset="//live.staticflickr.com/65535/48108493186_4a57522bdf_s.jpg 75w, //live.staticflickr.com/65535/48108493186_4a57522bdf_q.jpg 150w, //live.staticflickr.com/65535/48108493186_4a57522bdf_t.jpg 100w, //live.staticflickr.com/65535/48108493186_4a57522bdf_m.jpg 240w, //live.staticflickr.com/65535/48108493186_4a57522bdf_n.jpg 320w, //live.staticflickr.com/65535/48108493186_4a57522bdf_w.jpg 400w, //live.staticflickr.com/65535/48108493186_4a57522bdf.jpg 500w, //live.staticflickr.com/65535/48108493186_4a57522bdf_z.jpg 640w, //live.staticflickr.com/65535/48108493186_4a57522bdf_c.jpg 800w" class=""></a><h3>Chrome</h3><p>Perfect</p><a href='//flic.kr/p/2gibPkD/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/48108534863_4a57522bdf_c.jpg" width="800" srcset="//live.staticflickr.com/65535/48108534863_4a57522bdf_s.jpg 75w, //live.staticflickr.com/65535/48108534863_4a57522bdf_q.jpg 150w, //live.staticflickr.com/65535/48108534863_4a57522bdf_t.jpg 100w, //live.staticflickr.com/65535/48108534863_4a57522bdf_m.jpg 240w, //live.staticflickr.com/65535/48108534863_4a57522bdf_n.jpg 320w, //live.staticflickr.com/65535/48108534863_4a57522bdf_w.jpg 400w, //live.staticflickr.com/65535/48108534863_4a57522bdf.jpg 500w, //live.staticflickr.com/65535/48108534863_4a57522bdf_z.jpg 640w, //live.staticflickr.com/65535/48108534863_4a57522bdf_c.jpg 800w" class=""></a><h3>Firefox</h3><p>Perfect</p><a href='//flic.kr/p/2gica8i/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/65535/48108601427_531ecd9756_c.jpg" width="800" srcset="//live.staticflickr.com/65535/48108601427_531ecd9756_s.jpg 75w, //live.staticflickr.com/65535/48108601427_531ecd9756_q.jpg 150w, //live.staticflickr.com/65535/48108601427_531ecd9756_t.jpg 100w, //live.staticflickr.com/65535/48108601427_531ecd9756_m.jpg 240w, //live.staticflickr.com/65535/48108601427_531ecd9756_n.jpg 320w, //live.staticflickr.com/65535/48108601427_531ecd9756_w.jpg 400w, //live.staticflickr.com/65535/48108601427_531ecd9756.jpg 500w, //live.staticflickr.com/65535/48108601427_531ecd9756_z.jpg 640w, //live.staticflickr.com/65535/48108601427_531ecd9756_c.jpg 800w" class=""></a>]]></content>
<summary type="html"><p>This post is about IDN (International Domain Names) and browser support in 2019, 5 years after the previous one. And hopefully, I’ll publ</summary>
<category term="idn" scheme="https://tpî.eu/tags/idn/"/>
<category term="browser" scheme="https://tpî.eu/tags/browser/"/>
<category term="support" scheme="https://tpî.eu/tags/support/"/>
</entry>
<entry>
<title>Switching to Emoji Theme</title>
<link href="https://tpî.eu/2018/07/31/Switching-To-Emoji-Theme/"/>
<id>https://tpî.eu/2018/07/31/Switching-To-Emoji-Theme/</id>
<published>2018-07-31T00:00:00.000Z</published>
<updated>2018-07-31T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>In the past weeks, I have transformed the theme of this blog to use emoji instead of FontAwesome icons (FA).</p><p>FA icons were of great service, but I was only using few of them, and it was needlessly adding latency and using bandwidth.</p><p>The latency was caused by the fact that the font needed to be loaded after the CSS was loaded, and the bandwidth usage was caused by the weight of the font file. See <a href="https://github.com/tomap/tpi2.eu/blob/975f9a247fff11d360085b0ef971f88e01f889df/themes/anodyne/source/fonts/fontawesome-webfont.woff2">the .woff2 file for example</a>: 72KB.</p><p>For the CSS and font files, I had two possibilities:</p><ul><li>either reference them from a CDN, and hope that they were already cached by the client. If not, the client had to download the font + the full CSS (31KB or 7KB gzipped)</li><li>or embed them in the site, and by using <a href="https://github.com/uncss/uncss">uncss</a> be able to strip down the CSS for the icons I was not using. Only a few bytes.</li></ul><p>I experimented both, and ended up using the 2nd option: embedding FontAwesome to leverage uncss with hexo-uncss.</p><p>Then I looked at the very few icons I was using, and realized I could use Emoji instead.</p><p>I made a map <a href="https://github.com/tomap/tpi2.eu/blob/0fe8e130dffd87a1d9e631637ddfb99b03f1d5d0/FontAwesomeToUnicode.md">FA <> Emoji</a>.<br>I found an equivalent for every icons except the brands (LinkedIn, Twitter, …).<br>For them, I switched from Font Icons to SVG of those icons (first using FA SVG’s, and then SVG from <a href="https://simpleicons.org/">SimpleIcons</a>, with a more permissive license CC Zero)</p><p>Here are the technical changes I made to do that:</p><h2>Emojis</h2><p>I used emojis for pages & posts titles and for the “tags” & “share” icons (see at the bottom of the page).</p><p>In the header of the post, instead of mentioning:</p><figure class="highlight md"><table><tr><td class="code"><pre><span class="line">icon: fa-fire</span><br></pre></td></tr></table></figure><p>I can use the emoji directly:</p><figure class="highlight md"><table><tr><td class="code"><pre><span class="line">icon: 🔥</span><br></pre></td></tr></table></figure><p>This will the reference the css <code>.ec-🔥</code> which will display the icon as a <code>content: '🔥'</code></p><p>I also had to change the open graph image used when sharing to Twitter or LinkedIn. For the, I found this service <a href="https://xn--i-7iqv272g.ws/emoji-image/%F0%9F%94%A5.png">https://i❤️.ws/emoji-image/🔥.png</a> which provides a png from an emoji. See <a href="https://medium.com/@Emoji_Domains/free-emoji-image-generator-api-c0b7eaefa586">the article on Medium</a> for more explanation.</p><p>By default, emoji are displayed with colors, see the fire or smiley 🔥 😊.<br>In order to keep something closer to FA, I applied a <code>filter: sepia(90%);</code> which gave them a nicer render.</p><h2>Brands</h2><p>For brands, I used the brand icons from <a href="http://SimpleIcons.org">SimpleIcons.org</a>. They have pretty much every brand you can ask for, and if they don’t have it, you can <a href="https://github.com/simple-icons/simple-icons/issues/625#issuecomment-361242420">ask for it</a>.</p><p>It’s a manual process: I copied the brand icon from they site, optimized it using <a href="https://jakearchibald.github.io/svgomg/">https://jakearchibald.github.io/svgomg/</a>, manually removed some more useless stuff and paste them as inline background images.</p><p>Original SVG (twitter)</p><figure class="highlight svg"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">svg</span> <span class="attr">aria-labelledby</span>=<span class="string">"simpleicons-twitter-icon"</span> <span class="attr">role</span>=<span class="string">"img"</span> <span class="attr">viewBox</span>=<span class="string">"0 0 24 24"</span> <span class="attr">xmlns</span>=<span class="string">"http://www.w3.org/2000/svg"</span>></span><span class="tag"><<span class="name">title</span> <span class="attr">id</span>=<span class="string">"simpleicons-twitter-icon"</span>></span>Twitter icon<span class="tag"></<span class="name">title</span>></span><span class="tag"><<span class="name">path</span> <span class="attr">d</span>=<span class="string">"M23.954 4.569c-.885.389-1.83.654-2.825.775 1.014-.611 1.794-1.574 2.163-2.723-.951.555-2.005.959-3.127 1.184-.896-.959-2.173-1.559-3.591-1.559-2.717 0-4.92 2.203-4.92 4.917 0 .39.045.765.127 1.124C7.691 8.094 4.066 6.13 1.64 3.161c-.427.722-.666 1.561-.666 2.475 0 1.71.87 3.213 2.188 4.096-.807-.026-1.566-.248-2.228-.616v.061c0 2.385 1.693 4.374 3.946 4.827-.413.111-.849.171-1.296.171-.314 0-.615-.03-.916-.086.631 1.953 2.445 3.377 4.604 3.417-1.68 1.319-3.809 2.105-6.102 2.105-.39 0-.779-.023-1.17-.067 2.189 1.394 4.768 2.209 7.557 2.209 9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63.961-.689 1.8-1.56 2.46-2.548l-.047-.02z"</span>/></span><span class="tag"></<span class="name">svg</span>></span></span><br></pre></td></tr></table></figure><p>Resulting CSS (in Stylus):</p><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.ic-twitter</span></span><br><span class="line"> <span class="attribute">background-image</span> url("data:image/svg+xml;utf8,<<span class="selector-tag">svg</span> viewBox='<span class="number">0</span> <span class="number">0</span> <span class="number">24</span> <span class="number">24</span>' xmlns='http://www.w3.org/<span class="number">2000</span>/svg<span class="string">'><path d='</span>M23.<span class="number">954</span> <span class="number">4.569</span>c-.<span class="number">885.389</span>-<span class="number">1.83</span>.<span class="number">654</span>-<span class="number">2.825</span>.<span class="number">775</span> <span class="number">1.014</span>-.<span class="number">611</span> <span class="number">1.794</span>-<span class="number">1.574</span> <span class="number">2.163</span>-<span class="number">2.723</span>-.<span class="number">951.555</span>-<span class="number">2.005</span>.<span class="number">959</span>-<span class="number">3.127</span> <span class="number">1.184</span>-.<span class="number">896</span>-.<span class="number">959</span>-<span class="number">2.173</span>-<span class="number">1.559</span>-<span class="number">3.591</span>-<span class="number">1.559</span>-<span class="number">2.717</span> <span class="number">0</span>-<span class="number">4.92</span> <span class="number">2.203</span>-<span class="number">4.92</span> <span class="number">4.917</span> <span class="number">0</span> .<span class="number">39.045</span>.<span class="number">765.127</span> <span class="number">1.124</span>C7.<span class="number">691</span> <span class="number">8.094</span> <span class="number">4.066</span> <span class="number">6.13</span> <span class="number">1.64</span> <span class="number">3.161</span>c-.<span class="number">427.722</span>-.<span class="number">666</span> <span class="number">1.561</span>-.<span class="number">666</span> <span class="number">2.475</span> <span class="number">0</span> <span class="number">1.71</span>.<span class="number">87</span> <span class="number">3.213</span> <span class="number">2.188</span> <span class="number">4.096</span>a4.<span class="number">9044</span> <span class="number">4.9044</span> <span class="number">0</span> <span class="number">0</span> <span class="number">1</span>-<span class="number">2.228</span>-.<span class="number">616</span>v.<span class="number">061</span>c0 <span class="number">2.385</span> <span class="number">1.693</span> <span class="number">4.374</span> <span class="number">3.946</span> <span class="number">4.827</span>a4.<span class="number">9631</span> <span class="number">4.9631</span> <span class="number">0</span> <span class="number">0</span> <span class="number">1</span>-<span class="number">2.212</span>.<span class="number">085</span>c.<span class="number">631</span> <span class="number">1.953</span> <span class="number">2.445</span> <span class="number">3.377</span> <span class="number">4.604</span> <span class="number">3.417</span>-<span class="number">1.68</span> <span class="number">1.319</span>-<span class="number">3.809</span> <span class="number">2.105</span>-<span class="number">6.102</span> <span class="number">2.105</span>-.<span class="number">39</span> <span class="number">0</span>-.<span class="number">779</span>-.<span class="number">023</span>-<span class="number">1.17</span>-.<span class="number">067</span> <span class="number">2.189</span> <span class="number">1.394</span> <span class="number">4.768</span> <span class="number">2.209</span> <span class="number">7.557</span> <span class="number">2.209</span> <span class="number">9.054</span> <span class="number">0</span> <span class="number">13.999</span>-<span class="number">7.496</span> <span class="number">13.999</span>-<span class="number">13.986</span> <span class="number">0</span>-.<span class="number">209</span> <span class="number">0</span>-.<span class="number">42</span>-.<span class="number">015</span>-.<span class="number">63.961</span>-.<span class="number">689</span> <span class="number">1.8</span>-<span class="number">1.56</span> <span class="number">2.46</span>-<span class="number">2.548</span>l-.<span class="number">047</span>-.<span class="number">02</span>z<span class="string">'/></svg>")</span></span><br></pre></td></tr></table></figure><p>And here we are:</p><ul><li>Home page HTML is now 9.91kb from 9.97kb</li><li>CSS is now 8.96kb from 7.84kb</li><li>no more Font file downloaded (was 75.9kb)</li></ul><p>So it’s lighter overall, but a bit bigger for CSS.</p><p>Next step will be to create a nice theme and share it on <a href="https://hexo.io/themes/">https://hexo.io/themes/</a></p>]]></content>
<summary type="html"><p>In the past weeks, I have transformed the theme of this blog to use emoji instead of FontAwesome icons (FA).</p>
<p>FA icons were of grea</summary>
<category term="theme" scheme="https://tpî.eu/tags/theme/"/>
<category term="emoji" scheme="https://tpî.eu/tags/emoji/"/>
</entry>
<entry>
<title>New Comment System</title>
<link href="https://tpî.eu/2018/01/12/New-comment-system/"/>
<id>https://tpî.eu/2018/01/12/New-comment-system/</id>
<published>2018-01-12T00:00:00.000Z</published>
<updated>2018-01-12T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>I dropped Disqus comment system from this site after reading this news: <a href="https://blog.disqus.com/disqus-and-zeta">https://blog.disqus.com/disqus-and-zeta</a> and the wikipedia page which raises many privacy concerns: <a href="https://en.wikipedia.org/wiki/Disqus#Criticism_and_privacy_concerns">https://en.wikipedia.org/wiki/Disqus#Criticism_and_privacy_concerns</a><br>Also, with this change, I have a fully static site, without any javascript.</p><p>So, I looked around to find a third party websites with a bit guarantee but could not find any.<br>So I decided to switch to asking reader to comment on third party sites like Twitter, LinkedIn and Github.</p><p>So I created links (see bellow this post) to share comments via those sites.</p><h2>Github</h2><p>For Github, I could not find an official documentation describing the url format to create an issue with some fields prefilled but I found this: <a href="https://github.com/isaacs/github/issues/99">https://github.com/isaacs/github/issues/99</a> which explains how to prefill the title, body & label:<br><code>https://github.com/account/repo/issues/new?labels=mylabel&title=myTitle&body=myBody</code><br>Note that you can’t prefill a random label. The label must b part of the project label list: <a href="https://help.github.com/articles/creating-a-label/">https://help.github.com/articles/creating-a-label/</a></p><p>And here is the result:</p><a href='//flic.kr/p/DKmM1z/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/4766/24775863397_eb91b824dc_b.jpg" width="1009" srcset="//live.staticflickr.com/4766/24775863397_eb91b824dc_s.jpg 75w, //live.staticflickr.com/4766/24775863397_eb91b824dc_q.jpg 150w, //live.staticflickr.com/4766/24775863397_eb91b824dc_t.jpg 100w, //live.staticflickr.com/4766/24775863397_eb91b824dc_m.jpg 240w, //live.staticflickr.com/4766/24775863397_eb91b824dc_n.jpg 320w, //live.staticflickr.com/4766/24775863397_eb91b824dc_w.jpg 400w, //live.staticflickr.com/4766/24775863397_eb91b824dc.jpg 500w, //live.staticflickr.com/4766/24775863397_eb91b824dc_z.jpg 640w, //live.staticflickr.com/4766/24775863397_eb91b824dc_c.jpg 800w, //live.staticflickr.com/4766/24775863397_eb91b824dc_b.jpg 1009w" class=""></a><h2>Twitter</h2><p>For Twitter, there is a nice documentation: <a href="https://developer.twitter.com/en/docs/twitter-for-websites/web-intents/overview">https://developer.twitter.com/en/docs/twitter-for-websites/web-intents/overview</a></p><p>Here is the URL: <code>https://twitter.com/intent/tweet?url=https://example.com/myPage&via=tomap</code></p><p>And here is the result:</p><figure class="highlight md"><table><tr><td class="code"><pre><span class="line">{% flickr 27866246469 %}</span><br></pre></td></tr></table></figure><p>To have a nice “card”, I had to add a few missing Open Graph tags:</p><ul><li><p><code>image</code>: I pointed to the PNG equivalent of the post icon (Font-Awesome) using this repository: <a href="https://github.com/encharm/Font-Awesome-SVG-PNG">https://github.com/encharm/Font-Awesome-SVG-PNG</a> which provides Png equivalent of the Font-Awesome icons.<br>I relied on <a href="https://rawgit.com/">https://rawgit.com/</a> which converts Git Asset url to urls that can be included in your site:</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><% if (page.icon || theme.default_post_icon){ %></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">property</span>=<span class="string">"og:image"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">content</span>=<span class="string">"https://raw.githubusercontent.com/encharm/Font-Awesome-SVG-PNG/30dda99e/black/png/256/<%= (page.icon || theme.default_post_icon).substr(3) %>.png"</span> /></span></span><br><span class="line"><% } %></span><br></pre></td></tr></table></figure><p>See <a href="https://github.com/tomap/tpi2.eu/blob/fd279cc51590975d97e1030bffc836b8db8611dc/themes/anodyne/layout/_partial/head.ejs#L48">https://github.com/tomap/tpi2.eu/blob/master/themes/anodyne/layout/_partial/head.ejs#L48</a></p></li><li><p><code>url</code>: I used <a href="https://hexo.io/docs/variables.html#Page-Variables">Hexo variable</a> <code>permalink</code>:</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="comment"><!-- Page permalink --></span></span><br><span class="line"><% if (page.permalink){ %></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">property</span>=<span class="string">"og:url"</span> <span class="attr">content</span>=<span class="string">"<%= page.permalink %>"</span> /></span></span><br><span class="line"><% } %></span><br></pre></td></tr></table></figure></li></ul><h2>LinkedIn</h2><p>Open Graph tags were also needed for LinkedIn but what already existed in the theme and what I added for Twitter was enough.</p><p>To create the url, I used this documentation: <a href="https://developer.linkedin.com/docs/share-on-linkedin#">https://developer.linkedin.com/docs/share-on-linkedin#</a> > Click on “Customized URL” and here is the result: <code>https://www.linkedin.com/shareArticle?mini=true&url=https://tpî.eu/2018/01/10/New-comment-system/</code></p><p>And here is the result:</p><a href='//flic.kr/p/223292k/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/4666/38747904405_4dcc2eece6_o.png" width="804" srcset="//live.staticflickr.com/4666/38747904405_abbfe2754c_s.jpg 75w, //live.staticflickr.com/4666/38747904405_abbfe2754c_q.jpg 150w, //live.staticflickr.com/4666/38747904405_abbfe2754c_t.jpg 100w, //live.staticflickr.com/4666/38747904405_abbfe2754c_m.jpg 240w, //live.staticflickr.com/4666/38747904405_abbfe2754c_n.jpg 320w, //live.staticflickr.com/4666/38747904405_abbfe2754c_w.jpg 400w, //live.staticflickr.com/4666/38747904405_abbfe2754c.jpg 500w, //live.staticflickr.com/4666/38747904405_abbfe2754c_z.jpg 640w, //live.staticflickr.com/4666/38747904405_abbfe2754c_c.jpg 800w, //live.staticflickr.com/4666/38747904405_4dcc2eece6_o.png 804w" class=""></a><p>Then all I had to do was to add those links instead of Disqus comment system. See <a href="https://github.com/tomap/tpi2.eu/blob/fd279cc51590975d97e1030bffc836b8db8611dc/themes/anodyne/layout/_partial/comments.ejs#L3">https://github.com/tomap/tpi2.eu/blob/master/themes/anodyne/layout/_partial/comments.ejs#L3</a></p>]]></content>
<summary type="html"><p>I dropped Disqus comment system from this site after reading this news: <a href="https://blog.disqus.com/disqus-and-zeta">https://blog.di</summary>
<category term="github" scheme="https://tpî.eu/tags/github/"/>
<category term="comment" scheme="https://tpî.eu/tags/comment/"/>
<category term="disqus" scheme="https://tpî.eu/tags/disqus/"/>
<category term="linkedin" scheme="https://tpî.eu/tags/linkedin/"/>
<category term="twitter" scheme="https://tpî.eu/tags/twitter/"/>
</entry>
<entry>
<title>Testing Firebase hosting</title>
<link href="https://tpî.eu/2017/12/20/Testing-Firebase-hosting/"/>
<id>https://tpî.eu/2017/12/20/Testing-Firebase-hosting/</id>
<published>2017-12-20T00:00:00.000Z</published>
<updated>2017-12-20T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>Using Travis CI, I tried deploying this hexo site to <a href="https://firebase.google.com/products/hosting/">firebase hosting</a>.</p><p>It includes 1Gb of storage and 10 Gb of data transfer monthly. So it would be a huge increase compared to OVH Start1m plan (also free).</p><p>It also supports Custom domain (So I could host this site or a subdomain on Firebase) & free SSL certificates.</p><p>I used the doc on travis to do that: <a href="https://docs.travis-ci.com/user/deployment/firebase/">https://docs.travis-ci.com/user/deployment/firebase/</a></p><p>Then I tried to generate encryption keys using travis command line:<br><a href="https://docs.travis-ci.com/user/encryption-keys/">https://docs.travis-ci.com/user/encryption-keys/</a></p><p>Unfortunately, it failed:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ travis</span><br><span class="line">-bash: /usr/local/bin/travis: /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby: bad interpreter: No such file or directory</span><br></pre></td></tr></table></figure><p>From what I read here and there, it’s an issue with the last MacOS update (High Sierra).</p><p>Here is the fix:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> gem install -n /usr/local/bin travis</span><br></pre></td></tr></table></figure><p>Once this is fixed, it is possible to generate your encryption key:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">travis encrypt <span class="string">"xyz<my ci key>wxy"</span> --add deploy.token</span><br></pre></td></tr></table></figure><p>Note that this will produce a warning that you can ignore.</p><p>Here is the full .travis.yml file:</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="attr">language:</span> <span class="string">node_js</span></span><br><span class="line"><span class="attr">node_js:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">node</span></span><br><span class="line"><span class="attr">git:</span></span><br><span class="line"> <span class="attr">depth:</span> <span class="number">2</span></span><br><span class="line"><span class="attr">branches:</span></span><br><span class="line"> <span class="attr">only:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">master</span></span><br><span class="line"><span class="attr">before_install:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">npm</span> <span class="string">install</span> <span class="string">-g</span> <span class="string">hexo-cli</span></span><br><span class="line"><span class="attr">install:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">npm</span> <span class="string">install</span></span><br><span class="line"><span class="attr">script:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">hexo</span> <span class="string">generate</span></span><br><span class="line"><span class="attr">before_deploy:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">cp</span> <span class="string">firebase/robots.txt</span> <span class="string">public/robots.txt</span></span><br><span class="line"><span class="attr">deploy:</span></span><br><span class="line"> <span class="attr">skip_cleanup:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">provider:</span> <span class="string">firebase</span></span><br><span class="line"> <span class="attr">token:</span></span><br><span class="line"> <span class="attr">secure:</span> <span class="string">tEG0...V9g=</span></span><br></pre></td></tr></table></figure><p>The current version is stored here: <a href="https://github.com/tomap/tpi2.eu/blob/b4a196bef3b788a73d44cbdfb45405dc10dc5c63/.travis.yml">https://github.com/tomap/tpi2.eu/blob/master/.travis.yml</a></p><p>And the result can be seen here: <a href="https://tpi-eu.firebaseapp.com/">https://tpi-eu.firebaseapp.com/</a> (I added a specific robots.txt to disallow search engine indexing)</p><p>Travis build logs are available here: <a href="https://travis-ci.org/tomap/tpi2.eu">https://travis-ci.org/tomap/tpi2.eu</a></p>]]></content>
<summary type="html"><p>Using Travis CI, I tried deploying this hexo site to <a href="https://firebase.google.com/products/hosting/">firebase hosting</a>.</p>
<p</summary>
<category term="hexo" scheme="https://tpî.eu/tags/hexo/"/>
<category term="firebase" scheme="https://tpî.eu/tags/firebase/"/>
<category term="hosting" scheme="https://tpî.eu/tags/hosting/"/>
<category term="travis" scheme="https://tpî.eu/tags/travis/"/>
</entry>
<entry>
<title>Site improvements</title>
<link href="https://tpî.eu/2017/12/18/Site-improvements/"/>
<id>https://tpî.eu/2017/12/18/Site-improvements/</id>
<published>2017-12-18T00:00:00.000Z</published>
<updated>2017-12-18T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>In the last weeks, I have slightly modified this site to bring the following nice improvements:</p><ul><li>Remove most of the JavaScript (Except to launch Disqus. See <a href="https://github.com/tomap/tpi2.eu/blob/a3cbd3a1ed1fd72b483a010f102a7f1e13d98624/themes/anodyne/layout/_partial/comments.ejs#L4">https://github.com/tomap/tpi2.eu/blob/master/themes/anodyne/layout/_partial/comments.ejs</a>)</li><li>Add HSTS header & nosniff header. See <a href="https://github.com/tomap/tpi2.eu/blob/master/assets/.htaccess#L416">https://github.com/tomap/tpi2.eu/blob/master/assets/.htaccess#L416</a></li><li>Include the assets I use (namely FontAwesome & tachyon) in the dev dependencies of package.json so that I can easily spot if thy are updated. Either here: <a href="https://david-dm.org/tomap/tpi2.eu?type=dev">https://david-dm.org/tomap/tpi2.eu?type=dev</a> or using <a href="https://marketplace.visualstudio.com/items?itemName=pflannery.vscode-versionlens">Version Lens</a></li><li>Enable <a href="https://github.com/mamboer/hexo-filter-cleanup">hexo-filter-cleanup</a> plugin which compresses my HTML & CSS</li></ul><p>After all that this site weights less than 200 KB!</p><p>Next I’ll post about Firebase :)</p>]]></content>
<summary type="html"><p>In the last weeks, I have slightly modified this site to bring the following nice improvements:</p>
<ul>
<li>Remove most of the JavaScrip</summary>
<category term="hexo" scheme="https://tpî.eu/tags/hexo/"/>
<category term="nodejs" scheme="https://tpî.eu/tags/nodejs/"/>
<category term="security" scheme="https://tpî.eu/tags/security/"/>
</entry>
<entry>
<title>Travis CI Setup attempt</title>
<link href="https://tpî.eu/2017/04/24/Travis-setup/"/>
<id>https://tpî.eu/2017/04/24/Travis-setup/</id>
<published>2017-04-24T00:00:00.000Z</published>
<updated>2017-04-24T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>I wanted to be able to publish this site using <a href="https://travis-ci.org/">Travis</a> instead of AppVeyor, just to try.</p><p>I found this with Google: <a href="https://github.com/jkeylu/deploy-hexo-site-by-travis-ci/blob/master/_travis.yml">https://github.com/jkeylu/deploy-hexo-site-by-travis-ci/blob/master/_travis.yml</a></p><p>I’m not pushing to GitHub pages but to a FTP so I had to slightly modify the yml following <a href="https://docs.travis-ci.com/user/deployment/custom/#FTP">this documentation from Travis site</a>:</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="attr">env:</span></span><br><span class="line"> <span class="attr">global:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">"FTP_USER=user"</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">"FTP_PASSWORD=password"</span></span><br><span class="line"><span class="attr">after_success:</span></span><br><span class="line"> <span class="string">"curl --ftp-create-dirs -T uploadfilename -u $FTP_USER:$FTP_PASSWORD ftp://sitename.com/directory/myfile"</span></span><br></pre></td></tr></table></figure><p>Unfortunately, there are multiple issues with this:</p><ul><li>One, it only uploads one file, not a full folder</li><li>Two, if the upload fails, it does not break the build.</li></ul><p>To upload a full folder, I found this <a href="https://stackoverflow.com/a/14020013/383029">on Stack Overflow</a>:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">find mydir -<span class="built_in">type</span> f -<span class="built_in">exec</span> curl -u xxx:psw --ftp-create-dirs -T {} ftp://192.168.1.158/public/demon_test/{} \;</span><br></pre></td></tr></table></figure><p>It is closer but I had to replace <strong>-exec</strong> parameter by piping to <a href="https://www.computerhope.com/unix/xargs.htm">xargs</a> because <strong>-exec</strong> does not return a -1 exit code in case of failure.</p><p>For escaping data to yaml, I used <a href="https://www.json2yaml.com/">https://www.json2yaml.com/</a></p><p>In then end, the solution of using after_success is not cool, because in my case, that I consider build is publishing the site to the ftp. So if this step fails, I want the build to fail.</p><p>With after_success, the build is still green if this step fails. See <a href="https://docs.travis-ci.com/user/customizing-the-build/#Breaking-the-Build">https://docs.travis-ci.com/user/customizing-the-build/#Breaking-the-Build</a></p><p>I also tried that: <a href="https://stackoverflow.com/questions/3790454/in-yaml-how-do-i-break-a-string-over-multiple-lines">https://stackoverflow.com/questions/3790454/in-yaml-how-do-i-break-a-string-over-multiple-lines</a><br>(did not worked)</p><p>So, in the end, I dropped Travis.</p>]]></content>
<summary type="html"><p>I wanted to be able to publish this site using <a href="https://travis-ci.org/">Travis</a> instead of AppVeyor, just to try.</p>
<p>I fou</summary>
<category term="github" scheme="https://tpî.eu/tags/github/"/>
<category term="travis" scheme="https://tpî.eu/tags/travis/"/>
</entry>
<entry>
<title>Flickr restored</title>
<link href="https://tpî.eu/2017/02/26/flickr-restored/"/>
<id>https://tpî.eu/2017/02/26/flickr-restored/</id>
<published>2017-02-26T00:00:00.000Z</published>
<updated>2017-02-26T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>I restored Flickr images on my site:</p><a href='//flic.kr/p/S5dGBW/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/505/32865582372_504939cc58_b.jpg" width="1024" srcset="//live.staticflickr.com/505/32865582372_504939cc58_s.jpg 75w, //live.staticflickr.com/505/32865582372_504939cc58_q.jpg 150w, //live.staticflickr.com/505/32865582372_504939cc58_t.jpg 100w, //live.staticflickr.com/505/32865582372_504939cc58_m.jpg 240w, //live.staticflickr.com/505/32865582372_504939cc58_n.jpg 320w, //live.staticflickr.com/505/32865582372_504939cc58_w.jpg 400w, //live.staticflickr.com/505/32865582372_504939cc58.jpg 500w, //live.staticflickr.com/505/32865582372_504939cc58_z.jpg 640w, //live.staticflickr.com/505/32865582372_504939cc58_c.jpg 800w, //live.staticflickr.com/505/32865582372_504939cc58_b.jpg 1024w" class=""></a><p>It is less convenient than the <a href="https://github.com/tomap/docpad-plugin-flickrimages">plugin I developed for Docpad</a> because I have to manually upload images to Flickr myself. Once it is uploaded, I just grab the picture id and stick it inside a tag:</p><figure class="highlight md"><table><tr><td class="code"><pre><span class="line">{% flickr 32865582372 %}</span><br></pre></td></tr></table></figure><p>Also, I still have to add something like fancyBox which btw seem to have a <a href="https://fancyapps.com/fancybox/3/">version 3 available</a>.</p><p>So my todo list for this site:</p><ul><li>add fancyBox</li><li>restore images on previous posts</li><li>maybe test a bit Travis just in case AppVeyor breaks</li><li><s>Install a SSL Certificate with CloudFlare</s> <a href="https://arstechnica.com/security/2017/02/serious-cloudflare-bug-exposed-a-potpourri-of-secret-customer-data/">or NOT</a></li><li>Add a button to display Disqus comments. So they are not loaded by default.</li><li>Publish my resume :)</li></ul>]]></content>
<summary type="html"><p>I restored Flickr images on my site:</p>
<a href='//flic.kr/p/S5dGBW/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.</summary>
<category term="flickr" scheme="https://tpî.eu/tags/flickr/"/>
</entry>
<entry>
<title>AppVeyor Setup</title>
<link href="https://tpî.eu/2017/02/19/AppVeyor-Setup/"/>
<id>https://tpî.eu/2017/02/19/AppVeyor-Setup/</id>
<published>2017-02-19T00:00:00.000Z</published>
<updated>2017-02-19T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>I just configured AppVeyor Continuous Deployment.</p><p>To do that, I went to <a href="https://www.appveyor.com/">AppVeyor</a>, <a href="https://ci.appveyor.com/projects">logged in</a>, then <a href="https://ci.appveyor.com/projects">Project</a> and click <a href="https://ci.appveyor.com/projects/new">New Project link on top</a>, then pick the GitHub Repository that you want to build and deploy (<a href="https://github.com/tomap/tpi2.eu">github.com/tomap/tpi2.eu</a> in my case).</p><p>After that, in the new project, I went to settings, <a href="https://ci.appveyor.com/project/tomap/tpi2-eu/settings">https://ci.appveyor.com/project/tomap/tpi2-eu/settings</a>, in the Build part, I choose Script and added the following:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install</span><br><span class="line">npm -g install hexo-cli</span><br><span class="line">hexo generate</span><br></pre></td></tr></table></figure><p>that generates the site in the <strong>public</strong> folder.</p><p>Then I went to Artifact and selected the content of this folder: <code>public\**\*</code></p><p>Then I went to Deploy, choose FTP deployment. And that when issues started :(</p><p>FTP is always something messy. Active or not. FTP, SFTP or FTPS. I tried them all without success.<br>Then start googling and although my issue was not exactly the same, I tried the suggestion from <a href="https://help.appveyor.com/discussions/problems/3236-cant-deploy-via-ftp-because-of-error-message-450">AppVeyor Support Forum</a> and guess what, it worked!<br>The solution was to enable a “Beta FTP” option, … of course.</p><p>Well, at first, I wanted to use SFTP (you know, for security, …), which is supposed to be enabled on my OVH account, but it did not work. So that’s for another day.</p><p>To test the deployment, I set the FTP folder to <strong>/www/test/</strong>. Strangely, AppVeyor decided that the best folder to deploy my files was: <strong>/www/test/**public**/…</strong> instead of directly <strong>/www/test/…</strong> it was one folder too deep.</p><p>I googled again and found <a href="https://www.appveyor.com/docs/packaging-artifacts/#pushing-artifacts-from-scripts">that AppVeyor documentation page that helped me</a>. So my solution was to remove the artifact and instead use a PowerShell to publish the files so that their path was <strong>index.html</strong> instead of <strong>public/index.html</strong>.</p><p>After some trial and errors (~40 of them), I end up with something working. This is all nice and great, but I don’t like having all the configuration in AppVeyor, I prefer to have it in my Git Repository, so I can reproduce it somewhere else and I can share it also :)</p><p>Here is the exported version of my configuration: <strong>AppVeyor.yml</strong></p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="number">1.1</span><span class="string">.{build}</span></span><br><span class="line"><span class="attr">build_script:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">cmd:</span> <span class="string">>-</span></span><br><span class="line"><span class="string"> npm install</span></span><br><span class="line"><span class="string"> npm -g install hexo-cli</span></span><br><span class="line"><span class="string"> hexo generate</span></span><br><span class="line"><span class="string"></span><span class="attr">test:</span> <span class="string">off</span></span><br><span class="line"><span class="attr">before_deploy:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">ps:</span> <span class="string">$root</span> <span class="string">=</span> <span class="string">Resolve-Path</span> <span class="string">.\public;</span> [<span class="string">IO.Directory</span>]<span class="string">::GetFiles($root.Path,</span> <span class="string">'*.*'</span><span class="string">,</span> <span class="string">'AllDirectories'</span><span class="string">)</span> <span class="string">|</span> <span class="string">%</span> { <span class="string">Push-AppveyorArtifact</span> <span class="string">$_</span> <span class="string">-FileName</span> <span class="string">$_.Substring($root.Path.Length</span> <span class="string">+</span> <span class="number">1</span><span class="string">)</span> <span class="string">-DeploymentName</span> <span class="string">www</span> }</span><br><span class="line"><span class="attr">deploy:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">provider:</span> <span class="string">FTP</span></span><br><span class="line"> <span class="attr">host:</span> <span class="string">ftp.cluster014.hosting.ovh.net</span></span><br><span class="line"> <span class="attr">protocol:</span> <span class="string">ftp</span></span><br><span class="line"> <span class="attr">username:</span> <span class="string">xntprja</span></span><br><span class="line"> <span class="attr">password:</span></span><br><span class="line"> <span class="attr">secure:</span> <span class="string">xXShqZFpQ6Jeg8O86XJ8fiqe7AiTHWrFaS6ic4AVTtE=</span></span><br><span class="line"> <span class="attr">folder:</span> <span class="string">/www/test</span></span><br><span class="line"> <span class="attr">artifact:</span> <span class="string">www</span></span><br><span class="line"> <span class="attr">active_mode:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">beta:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">debug:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><p>It takes around 2 minutes to build (due to npm package restore and FTP upload, mainly).<br>You can see the latest logs here: <a href="https://ci.appveyor.com/project/tomap/tpi2-eu">https://ci.appveyor.com/project/tomap/tpi2-eu</a><br>And the AppVeyor.yaml is pushed on my repo: <a href="https://github.com/tomap/tpi2.eu/blob/b4a196bef3b788a73d44cbdfb45405dc10dc5c63/appveyor.yml">https://github.com/tomap/tpi2.eu/blob/master/appveyor.yml</a><br>I also added an indispensable badge in my <a href="https://github.com/tomap/tpi2.eu/blob/master/Readme.md">ReadMe</a>: <a href="https://ci.appveyor.com/project/tomap/tpi2-eu"><img src="https://ci.appveyor.com/api/projects/status/amvptl7n6hj3j8i6?svg=true" alt="Build status"></a></p><p>EDIT:<br>It seems that when using <strong>appveyor.yml</strong> to store the configuration, AppVeyor uses a different virtual machine with a lower version of NodeJS. So, google again, and I found <a href="https://www.appveyor.com/docs/lang/nodejs-iojs/">help in their support page again</a>, and I had to add:</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="string">...</span></span><br><span class="line"><span class="attr">install:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">ps:</span> <span class="string">Install-Product</span> <span class="string">node</span> <span class="string">''</span></span><br><span class="line"><span class="string">...</span></span><br></pre></td></tr></table></figure><p>Which should install latest version of node (7.x) :)</p>]]></content>
<summary type="html"><p>I just configured AppVeyor Continuous Deployment.</p>
<p>To do that, I went to <a href="https://www.appveyor.com/">AppVeyor</a>, <a href=</summary>
<category term="appveyor" scheme="https://tpî.eu/tags/appveyor/"/>
<category term="github" scheme="https://tpî.eu/tags/github/"/>
</entry>
<entry>
<title>V2 Published</title>
<link href="https://tpî.eu/2017/02/16/V2-published/"/>
<id>https://tpî.eu/2017/02/16/V2-published/</id>
<published>2017-02-16T00:00:00.000Z</published>
<updated>2017-02-16T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>V2 of this site has been published.</p><p>I also pushed it on <a href="https://github.com/tomap/tpi2.eu">GitHub</a>.</p><p>Also, OVH supports free https certificate with <a href="https://letsencrypt.org/">Let’s Encrypt</a>. Nothing complicated, just using the admin console. <a href="https://www.ovh.co.uk/news/articles/a2224.ovh-your-free-ssl-certificates-via-lets-encrypt">See their blog</a>.</p><p>Now I’ll configure automatic deployment with <a href="https://www.appveyor.com/">AppVeyor</a> and restore lost images :)</p>]]></content>
<summary type="html"><p>V2 of this site has been published.</p>
<p>I also pushed it on <a href="https://github.com/tomap/tpi2.eu">GitHub</a>.</p>
<p>Also, OVH su</summary>
<category term="github" scheme="https://tpî.eu/tags/github/"/>
<category term="hosting" scheme="https://tpî.eu/tags/hosting/"/>
<category term="letsencrypt" scheme="https://tpî.eu/tags/letsencrypt/"/>
</entry>
<entry>
<title>New site generator (take 2)</title>
<link href="https://tpî.eu/2017/02/10/new-blog-generator-2/"/>
<id>https://tpî.eu/2017/02/10/new-blog-generator-2/</id>
<published>2017-02-10T00:00:00.000Z</published>
<updated>2017-02-10T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>After trying Hugo, I tried <a href="https://hexo.io">Hexo</a>.</p><p>Like Docpad, Hexo is also based upon NodeJS.</p><p>Basically, you can write your posts in Markdown, HTML, <a href="http://www.embeddedjs.com/">EJS</a>, like with Docpad.</p><p>It has many themes. I picked <a href="https://github.com/klugjo/hexo-theme-anodyne">Anodyne</a> and simplified it.<br>Unlike Hugo, you can easily modify the theme’s CSS (or <a href="http://stylus-lang.com/">Stylus</a>), and when you generate the site, it also regenerate the CSS.</p><p>Next steps:</p><ul><li>Integrate this plugin <a href="https://github.com/visioncan/hexo-tag-flickr">https://github.com/visioncan/hexo-tag-flickr</a> to have images back</li><li>Setup AppVeyor to publish the site when committing</li><li>Setup SSL (with Cloudflare or OVH if it’s free)</li></ul><p>PS: <a href="https://github.com/tomap/tpi.eu">old site still exists on GitHub</a></p>]]></content>
<summary type="html"><p>After trying Hugo, I tried <a href="https://hexo.io">Hexo</a>.</p>
<p>Like Docpad, Hexo is also based upon NodeJS.</p>
<p>Basically, you </summary>
<category term="hexo" scheme="https://tpî.eu/tags/hexo/"/>
<category term="nodejs" scheme="https://tpî.eu/tags/nodejs/"/>
<category term="static" scheme="https://tpî.eu/tags/static/"/>
</entry>
<entry>
<title>New site generator (take 1)</title>
<link href="https://tpî.eu/2017/02/05/new-blog-generator-1/"/>
<id>https://tpî.eu/2017/02/05/new-blog-generator-1/</id>
<published>2017-02-05T00:00:00.000Z</published>
<updated>2017-02-05T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>I was trying to change the site generator to something simpler and that would also support including images hosted elsewhere.</p><p>After <a href="https://www.staticgen.com/">some research</a>, I found <a href="https://gohugo.io">Hugo</a>. Hugo is a static site generator using Go.</p><p>The change was simple.</p><p>I followed the <a href="https://gohugo.io/overview/quickstart/">quick start tutorial</a>, then picked a new theme: <a href="https://github.com/the2ne/hugo-frais">hugo-frais</a> and transferred posts from the old site.</p><p>Post headers are similar. Before:</p><figure class="highlight md"><table><tr><td class="code"><pre><span class="line">---</span><br><span class="line">title: My progress in setting this site up</span><br><span class="line">layout: post</span><br><span class="line">tags: [setup,progress,site]</span><br><span class="line"><span class="section">date: '2013-01-26'</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure><p>After:</p><figure class="highlight md"><table><tr><td class="code"><pre><span class="line">+++</span><br><span class="line">title= "My progress in setting this site up"</span><br><span class="line">tags= ["setup","progress","site"]</span><br><span class="line">date= "2013-01-26"</span><br><span class="line">+++</span><br></pre></td></tr></table></figure><p>Not so complicated.</p><p>However, in then end, I looked at the source code and found some ugliness:</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">base</span> <span class="attr">href</span>=<span class="string">"https://xn--tp-rja.eu/"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"generator"</span> <span class="attr">content</span>=<span class="string">"Hugo 0.18.1"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"canonical"</span> <span class="attr">href</span>=<span class="string">"https://xn--tp-rja.eu/"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">href</span>=<span class="string">"https://tp%C3%AE.eu/index.xml"</span> <span class="attr">rel</span>=<span class="string">"alternate"</span> <span class="attr">type</span>=<span class="string">"application/rss+xml"</span> <span class="attr">title</span>=<span class="string">"Thomas Piart"</span> /></span></span><br></pre></td></tr></table></figure><p>After some digging in the options, it found two options:</p><figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="attr">canonifyURLs</span> = <span class="literal">true</span></span><br><span class="line"><span class="attr">relativeURLs</span> = <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>they made some url relative, but some were still broken and here did not seem to be an easy way to solve that.</p><p>I raised some issues on github, but eventually choose not to use Hugo.</p>]]></content>
<summary type="html"><p>I was trying to change the site generator to something simpler and that would also support including images hosted elsewhere.</p>
<p>Afte</summary>
<category term="static" scheme="https://tpî.eu/tags/static/"/>
<category term="hugo" scheme="https://tpî.eu/tags/hugo/"/>
<category term="go" scheme="https://tpî.eu/tags/go/"/>
</entry>
<entry>
<title>IDN and browser support</title>
<link href="https://tpî.eu/2014/01/05/IDN-and-browser-support/"/>
<id>https://tpî.eu/2014/01/05/IDN-and-browser-support/</id>
<published>2014-01-05T00:00:00.000Z</published>
<updated>2014-01-05T00:00:00.000Z</updated>
<content type="html"><![CDATA[<P>This post is about IDN and browser support.</P><P>First of all, http does not support unicode :). To <i>do</i> unicode, they use a <b>punycode</b> to convert any domain name into it's ascii equivalent: for example, my domain <b>tpî.eu</b> is translated to <b>https://xn--tp-rja.eu/</b>.</P>I bought this IDN domain (tpî.eu), found this idea very clever until I found out that browser support wasn't so good. It goes from no support to little issues to perfect.</P><P>Those differences can be explained because IDN is new and viewed as a potential security threat.</P><P>For example, using similar characters in ascii and unicode, one could spoof the content of ebay.com and send you to еbay.com:You did not notice any differences? Have a better look: <u>ebay.com</u> and <u>еbay.com</u> . notice the difference between <b>e</b> and <b>е</b>: the last one is a Cyrillic character. (see <a href="https://en.wikipedia.org/wiki/IDN_homograph_attack">wikipedia about that</a>)</P><P>To avoid that, Registar are supposed to do some similarity check and reject possible spoof. Browser on the other side also have some kind of protection which varies from one browser to another. If the check fails, you'll see <i>safe</i> punycode.</P><P>Let's start:</P><h2>Firefox 19</h2><P>On Mac, <b>.eu</b> extension is not supposed to display IDN, so it's not part of a white list, so by default, you should see punycode. What I did was to add a key to about:config to add .eu to the white list:</p><a href='//flic.kr/p/SeaQeb/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/569/32966871092_619871c9dd_z.jpg" width="640" srcset="//live.staticflickr.com/569/32966871092_619871c9dd_s.jpg 75w, //live.staticflickr.com/569/32966871092_619871c9dd_q.jpg 150w, //live.staticflickr.com/569/32966871092_619871c9dd_t.jpg 100w, //live.staticflickr.com/569/32966871092_619871c9dd_m.jpg 240w, //live.staticflickr.com/569/32966871092_619871c9dd_n.jpg 320w, //live.staticflickr.com/569/32966871092_619871c9dd_w.jpg 400w, //live.staticflickr.com/569/32966871092_619871c9dd.jpg 500w, //live.staticflickr.com/569/32966871092_619871c9dd_z.jpg 640w" class=""></a><p>And here is the result:</p><a href='//flic.kr/p/Rbjagw/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/2840/32278189784_0e843d6d75_z.jpg" width="640" srcset="//live.staticflickr.com/2840/32278189784_0e843d6d75_s.jpg 75w, //live.staticflickr.com/2840/32278189784_0e843d6d75_q.jpg 150w, //live.staticflickr.com/2840/32278189784_0e843d6d75_t.jpg 100w, //live.staticflickr.com/2840/32278189784_0e843d6d75_m.jpg 240w, //live.staticflickr.com/2840/32278189784_0e843d6d75_n.jpg 320w, //live.staticflickr.com/2840/32278189784_0e843d6d75_w.jpg 400w, //live.staticflickr.com/2840/32278189784_0e843d6d75.jpg 500w, //live.staticflickr.com/2840/32278189784_0e843d6d75_z.jpg 640w" class=""></a><h2>Chrome on Android</h2><p>On Android, with Chrome: it does not work and shows punycode:</p><a href='//flic.kr/p/SgMZZ8/sizes/l' target='_blank' rel='noopener noreferrer'><img src="//live.staticflickr.com/3683/32996560941_2d9bcd97ab_z.jpg" width="640" srcset="//live.staticflickr.com/3683/32996560941_2d9bcd97ab_s.jpg 75w, //live.staticflickr.com/3683/32996560941_2d9bcd97ab_q.jpg 150w, //live.staticflickr.com/3683/32996560941_2d9bcd97ab_t.jpg 100w, //live.staticflickr.com/3683/32996560941_2d9bcd97ab_m.jpg 240w, //live.staticflickr.com/3683/32996560941_2d9bcd97ab_n.jpg 320w, //live.staticflickr.com/3683/32996560941_2d9bcd97ab_w.jpg 400w, //live.staticflickr.com/3683/32996560941_2d9bcd97ab.jpg 500w, //live.staticflickr.com/3683/32996560941_2d9bcd97ab_z.jpg 640w" class=""></a><p>Note: I wrote this article one year ago (in March 2013) and never took time to publish it :(</p>]]></content>
<summary type="html"><P>
This post is about IDN and browser support.
</P>
<P>
First of all, http does not support unicode :). To <i>do</i> unicode, they use a <</summary>
<category term="idn" scheme="https://tpî.eu/tags/idn/"/>
<category term="browser" scheme="https://tpî.eu/tags/browser/"/>
<category term="support" scheme="https://tpî.eu/tags/support/"/>
</entry>
<entry>
<title>Docpad and flickr</title>
<link href="https://tpî.eu/2013/03/10/Docpad-and-flickr/"/>
<id>https://tpî.eu/2013/03/10/Docpad-and-flickr/</id>
<published>2013-03-10T00:00:00.000Z</published>
<updated>2013-03-10T00:00:00.000Z</updated>
<content type="html"><![CDATA[<P>After my last post, I wanted to write something about IDN and browser support.<br>So I made lot's of screen shots about how well IDN is supported.<br>Then I figured that if I ever wanted to do more than one post with images on this blog, I had to host those images somewhere...<br>So, I started looking at some image hosting services, like google, 500px, ... and in the end, I choose flickr because they have an API, and I already had an account.<br>In order to avoid uploading images one by one and linking them to my post, I have tried to automate this process using a docpad plugin:</P><h2>docpad-plugin-flickrimages</h2><P>This plugin is a fork from <a href='https://github.com/docpad/docpad-plugin-associatedfiles/'>docpad-flickr-associatedfiles</a>.It's a start :).<br>Here is how it works:<ol><li>List all images in a dedicated folder</li><li>Call Flickr to get the list of images with a tag ("docpad" by default, but can be configured)</li><li>Get additional properties (url of images in various sizes,...)</li><li>If local image is not on flickr, upload it</li><li>If local image is on flickr, check the last update, and update the image if necessary</li><li>Remove local image from file that needs to be copied to the output directory</li><li>In any page, add a link to an image using document.getFlickrImage('my image.png')</li><li> this call will be replace by a reference to the image on flickr</li></ol>Currently some thing are not yet working:<ul><li>Update an image</li><li>There are no tests</li><li>Error cases are not working</li><li>...</li></ul></p><p>A little example:</p>[EDIT: Fev 2017: does not work yet in Hugo]<br>%- @getDocument().getFlickrImage('blog post code.png') %<br>%- @getDocument().getFlickrImage('beaglebone.jpg') %<br><P>Also: by default, getFlickrImage return a piece of HTML that works fine with fancybox.</p>]]></content>
<summary type="html"><P>
After my last post, I wanted to write something about IDN and browser support.<br>
So I made lot's of screen shots about how well IDN is</summary>
<category term="docpad" scheme="https://tpî.eu/tags/docpad/"/>
<category term="image" scheme="https://tpî.eu/tags/image/"/>
<category term="flickr" scheme="https://tpî.eu/tags/flickr/"/>
<category term="oauth" scheme="https://tpî.eu/tags/oauth/"/>
</entry>
<entry>
<title>CoffeeScript, Comments, DocPad, ...</title>
<link href="https://tpî.eu/2013/01/31/coffescript-comments-docpad/"/>
<id>https://tpî.eu/2013/01/31/coffescript-comments-docpad/</id>
<published>2013-01-31T00:00:00.000Z</published>
<updated>2013-01-31T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>Thanks to DocPad, I’m learning new languages:</p><ul><li>CoffeeScript is not so easy to learn, but a lot cleaner than plain JS.</li><li>Markdown is also very simple. Makes you focus on text, not on format. Like wiki syntax.</li><li><a href="http://coffeekup.org/">CoffeeKup</a> is interesting but very confusing when you practiced HTML for a long time.</li></ul><p>Originally, this site skeleton was a clone of <a href="https://BaLupton.com">Benjamin Lupton’s website</a>, but I made some changes.<br>Everything has been pushed to GitHub.</p><p>I also restored Disqus comments.</p><p>I am working on integrating a FTP module to Docpad in order to automatically upload new changes to <a href="http://xn--tp-rja.eu">tpî.eu</a>, without having to use a FTP Client.</p>]]></content>
<summary type="html"><p>Thanks to DocPad, I’m learning new languages:</p>
<ul>
<li>CoffeeScript is not so easy to learn, but a lot cleaner than plain JS.</li>
<l</summary>
<category term="setup" scheme="https://tpî.eu/tags/setup/"/>
<category term="progress" scheme="https://tpî.eu/tags/progress/"/>
<category term="site" scheme="https://tpî.eu/tags/site/"/>
</entry>
<entry>
<title>My progress in setting this site up</title>
<link href="https://tpî.eu/2013/01/26/progress-in-setting-this-site-up/"/>
<id>https://tpî.eu/2013/01/26/progress-in-setting-this-site-up/</id>
<published>2013-01-26T00:00:00.000Z</published>
<updated>2013-01-26T00:00:00.000Z</updated>
<content type="html"><![CDATA[<p>I’m making some progress.</p><p>Some context:</p><ul><li>I am hosted by OVH with a .eu domain (they have an offer for <a href="https://www.ovh.com/fr/domaines/doteu.xml">0.99 euros</a> until January 31)</li><li>I choose a 3 letters domain : <em><a href="http://xn--tp-rja.eu">tpî.eu</a></em> (IDN provide plenty of new <strong>short domain</strong> names!)</li><li>I am using DocPad to run this site, so no PHP, no SQL, no CGI, … which makes a lot of sense since I wouldn’t need a incredible database to store a few ‘static’ blog posts</li><li>After feeling so smart about this 3 letters domain, I found out that IDN are a lot messier than they look (I’ll do a separate post about that)</li></ul><p>To be continued :)</p>]]></content>
<summary type="html"><p>I’m making some progress.</p>
<p>Some context:</p>
<ul>
<li>I am hosted by OVH with a .eu domain (they have an offer for <a href="https:/</summary>
<category term="setup" scheme="https://tpî.eu/tags/setup/"/>
<category term="progress" scheme="https://tpî.eu/tags/progress/"/>
<category term="site" scheme="https://tpî.eu/tags/site/"/>
</entry>
<entry>
<title>This is my first news on this site</title>
<link href="https://tpî.eu/2013/01/20/first-news-on-this-site/"/>
<id>https://tpî.eu/2013/01/20/first-news-on-this-site/</id>
<published>2013-01-20T11:11:11.000Z</published>
<updated>2013-01-20T11:11:11.000Z</updated>
<content type="html"><![CDATA[<p>This is my first post…</p><p>I’m trying to set up this site properly.</p><p>My previous site was hosted on the domain <a href="http://piart.name">piart.name</a>. It’s still visible on <a href="http://archive.org">archive.org</a>: <a href="https://web.archive.org/web/20120424133117/http://piart.name/">https://web.archive.org/web/20120424133117/http://piart.name/</a> (last update 2012)</p><p>And before that I was on <a href="http://piartt.free.fr">piartt.free.fr</a> <a href="https://web.archive.org/web/20040902160747/http://piartt.free.fr/">https://web.archive.org/web/20040902160747/http://piartt.free.fr/</a> (last update in 2004)</p><p>And before that, I built freckle it was on <a href="https://web.archive.org/web/20021130195032/http://freckle.free.fr:80/">https://web.archive.org/web/20021130195032/http://freckle.free.fr:80/</a> (last update 2002)</p>]]></content>
<summary type="html"><p>This is my first post…</p>
<p>I’m trying to set up this site properly.</p>
<p>My previous site was hosted on the domain <a href="http://p</summary>
<category term="site" scheme="https://tpî.eu/tags/site/"/>
</entry>
</feed>