2023-09-20
{"requestId":"d2328daa...","level":"ERROR","timestamp":"2023-05-26T23:18:24.834Z","customerId":"01H1D5NGA5D689HD57CN5BZSG3","error":"ERR_SYS_FCKD"}
{"requestId":"c4fe5fdd...","level":"INFO","timestamp":"2023-05-26T23:18:29.966Z","customerId":"01H1D5MZAVPSXSXPTYADF4BDND","message":"transaction in progress"}
{"requestId":"69733d2e...","level":"ERROR","timestamp":"2023-05-26T23:18:33.570Z","customerId":"01H1D5MZAVPSXSXPTYADF4BDND","error":"ERR_SYS_FCKD"}
{"requestId":"2845fc48...","level":"ERROR","timestamp":"2023-05-26T23:18:40.489Z","customerId":"01H1D5KGHRV3SNJ71C4E920872","error":"ERR_SYS_FCKD"}
{"requestId":"3e3e49b5...","level":"ERROR","timestamp":"2023-05-26T23:18:50.277Z","customerId":"01H1D5KSG0K81CCAGTSTBXHFA6","error":"ERR_CLIENT_TIMEOUT"}
{"requestId":"f28abf98...","level":"ERROR","timestamp":"2023-05-26T23:18:56.575Z","customerId":"01H1D5K2JDVD941R6H67YEY0G8","error":"ERR_BUSY"}
{"requestId":"608e579f...","level":"ERROR","timestamp":"2023-05-26T23:19:04.529Z","customerId":"01H1D5KSG0K81CCAGTSTBXHFA6","error":"ERR_NOT_FOUND"}
{"requestId":"3125588e...","level":"ERROR","timestamp":"2023-05-26T23:19:11.514Z","customerId":"01H1D5MJ4XNJ580DMM8Y7T1HRW","error":"ERR_SERVER_TIMEOUT"}
{"requestId":"40fb0329...","level":"INFO","timestamp":"2023-05-26T23:19:13.536Z","customerId":"01H1D5N6HS2EMA900A2V6NQQ2Q","message":"user logged in"}
{"requestId":"564afe31...","level":"ERROR","timestamp":"2023-05-26T23:19:21.708Z","customerId":"01H1D5K2JDVD941R6H67YEY0G8","error":"ERR_CLIENT_TIMEOUT"}
import { readFile } from 'node:fs/promises'
const rawData = await readFile('logs.jsonl', 'utf-8')
const lines = rawData.split(/\n+/)
const messages = lines.map(line => line === '' ? {} : JSON.parse(line))
const errors = messages.filter(message => message.error === 'ERR_SYS_FCKD')
const errorsByCustomer = errors.reduce((acc, error) => {
if (!acc[error.customerId]) {
acc[error.customerId] = 0
}
acc[error.customerId]++
return acc
}, {})
console.log('Errors by customer:')
console.table(errorsByCustomer)
Errors by customer:
{
'01H1D5NGA5D689HD57CN5BZSG3': 109,
'01H1D5MZAVPSXSXPTYADF4BDND': 118,
'01H1D5KGHRV3SNJ71C4E920872': 118,
'01H1D5KSG0K81CCAGTSTBXHFA6': 95,
'01H1D5MJ4XNJ580DMM8Y7T1HRW': 103,
'01H1D5N6HS2EMA900A2V6NQQ2Q': 96,
'01H1D5M5RTKKK5SC4ADWAPK7Q0': 130,
'01H1D5K2JDVD941R6H67YEY0G8': 115,
'01H1D5KZ7GV7HK213XE3WV5GQX': 113
}
import { readFile } from 'node:fs/promises'
const rawData = await readFile('logs.jsonl', 'utf-8')
const lines = rawData.split(/\n+/)
const messages = lines.map(line => line === '' ? {} : JSON.parse(line))
const errors = messages.filter(message => message.error === 'ERR_SYS_FCKD')
const errorsByCustomer = errors.reduce((acc, error) => {
if (!acc[error.customerId]) {
acc[error.customerId] = 0
}
acc[error.customerId]++
return acc
}, {})
console.log('Errors by customer:')
console.table(errorsByCustomer)
import { readFile } from 'node:fs/promises'
const rawData = await readFile('logs.jsonl', 'utf-8')
const lines = rawData.split(/\n+/)
const messages = lines.map(line => line === '' ? {} : JSON.parse(line))
const errors = messages.filter(message => message.error === 'ERR_SYS_FCKD')
const errorsByCustomer = errors.reduce((acc, error) => {
if (!acc[error.customerId]) {
acc[error.customerId] = 0
}
acc[error.customerId]++
return acc
}, {})
console.log('Errors by customer:')
console.table(errorsByCustomer)
//
{
}
{"requestId":"d2328daa...","level":"ERROR","timestamp":"2023-05-26T23:18:24.834Z","customerId":"01H1D5NGA5D689HD57CN5BZSG3","error":"ERR_SYS_FCKD"}
{
}
{"requestId":"d2328daa...","level":"ERROR","timestamp":"2023-05-26T23:18:24.834Z","customerId":"01H1D5NGA5D689HD57CN5BZSG3","error":"ERR_SYS_FCKD"}
{
}
{"requestId":"d2328daa...","level":"ERROR","timestamp":"2023-05-26T23:18:24.834Z","customerId":"01H1D5NGA5D689HD57CN5BZSG3","error":"ERR_SYS_FCKD"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1
}
{"requestId":"c4fe5fdd...","level":"INFO","timestamp":"2023-05-26T23:18:29.966Z","customerId":"01H1D5MZAVPSXSXPTYADF4BDND","message":"transaction in progress"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1
}
{"requestId":"c4fe5fdd...","level":"INFO","timestamp":"2023-05-26T23:18:29.966Z","customerId":"01H1D5MZAVPSXSXPTYADF4BDND","message":"transaction in progress"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1
}
{"requestId":"69733d2e...","level":"ERROR","timestamp":"2023-05-26T23:18:33.570Z","customerId":"01H1D5MZAVPSXSXPTYADF4BDND","error":"ERR_SYS_FCKD"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1
}
{"requestId":"69733d2e...","level":"ERROR","timestamp":"2023-05-26T23:18:33.570Z","customerId":"01H1D5MZAVPSXSXPTYADF4BDND","error":"ERR_SYS_FCKD"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1
}
{"requestId":"69733d2e...","level":"ERROR","timestamp":"2023-05-26T23:18:33.570Z","customerId":"01H1D5MZAVPSXSXPTYADF4BDND","error":"ERR_SYS_FCKD"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1,
"01H1D5MZAVPSXSXPTYADF4BDND": 1
}
{"requestId":"2845fc48...","level":"ERROR","timestamp":"2023-05-26T23:18:40.489Z","customerId":"01H1D5NGA5D689HD57CN5BZSG3","error":"ERR_SYS_FCKD"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1,
"01H1D5MZAVPSXSXPTYADF4BDND": 1
}
{"requestId":"2845fc48...","level":"ERROR","timestamp":"2023-05-26T23:18:40.489Z","customerId":"01H1D5NGA5D689HD57CN5BZSG3","error":"ERR_SYS_FCKD"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 1,
"01H1D5MZAVPSXSXPTYADF4BDND": 1
}
{"requestId":"2845fc48...","level":"ERROR","timestamp":"2023-05-26T23:18:40.489Z","customerId":"01H1D5NGA5D689HD57CN5BZSG3","error":"ERR_SYS_FCKD"}
{
"01H1D5NGA5D689HD57CN5BZSG3": 2,
"01H1D5MZAVPSXSXPTYADF4BDND": 1
}
const array = ['foo', 'bar', 'baz']
for (const item of array) {
console.log(item)
}
Prints all the items in the array!
Does it need to be an array? 🤔
Output:
foo
bar
baz
const str = 'foo'
for (const item of str) {
console.log(item)
}
Output:
f
o
o
const obj = {
foo: 'bar',
baz: 'qux'
}
for (const item of obj) {
console.log(item)
}
Output: ⛔️ Uncaught TypeError: obj is not iterable
OMG `for ... of`
does not work with plain objects! 😱
const array = ['foo', 'bar', 'baz']
console.log(...array)
Output:
foo bar baz
spread syntax!
function * myGenerator () {
// generator body
yield 'someValue'
// ... do more stuff
}
const genObj = myGenerator()
genObj.next() // -> { done: false, value: 'someValue' }
function * fruitGen () {
yield '🍑'
yield '🍉'
yield '🍋'
yield '🥭'
}
const fruitGenObj = fruitGen()
console.log(fruitGenObj.next()) // { value: '🍑', done: false }
console.log(fruitGenObj.next()) // { value: '🍉', done: false }
console.log(fruitGenObj.next()) // { value: '🍋', done: false }
console.log(fruitGenObj.next()) // { value: '🥭', done: false }
console.log(fruitGenObj.next()) // { value: undefined, done: true }
function * fruitGen () {
yield '🍑'
yield '🍉'
yield '🍋'
yield '🥭'
}
const fruitGenObj = fruitGen()
// generator objects are iterable!
for (const fruit of fruitGenObj) {
console.log(fruit)
}
// 🍑
// 🍉
// 🍋
// 🥭
const iterator = {
next () {
return {
done: false,
value: "someValue"
}
}
}
function createCountdown (from) {
let nextVal = from
return {
next () {
if (nextVal < 0) {
return { done: true }
}
return {
done: false,
value: nextVal--
}
}
}
}
A factory function that creates an iterator
returns an object
... which has a next() method
... which returns an object with
done & value
const countdown = createCountdown(3)
console.log(countdown.next())
// { done: false, value: 3 }
console.log(countdown.next())
// { done: false, value: 2 }
console.log(countdown.next())
// { done: false, value: 1 }
console.log(countdown.next())
// { done: false, value: 0 }
console.log(countdown.next())
// { done: true }
function createCountdown (from) {
let nextVal = from
return {
next () {
if (nextVal < 0) {
return { done: true }
}
return {
done: false,
value: nextVal--
}
}
}
}
function * createCountdown (from) {
for (let i = from; i >= 0; i--) {
yield i
}
}
Equivalent code using generators
const iterable = {
[Symbol.iterator] () {
return {
// iterator
next () {
return {
done: false,
value: "someValue"
}
}
}
}
}
function createCountdown (from) {
let nextVal = from
return {
[Symbol.iterator]: () => ({
next () {
if (nextVal < 0) {
return { done: true }
}
return { done: false, value: nextVal-- }
}
})
}
}
[...createCountdown(3)] // [3,2,1,0]
for (const value of createCountdown(3)) {
console.log(value)
}
// 3
// 2
// 1
// 0
function createCountdown (from) {
let nextVal = from
return {
[Symbol.iterator]: () => ({
next () {
if (nextVal < 0) {
return { done: true }
}
return {
done: false,
value: nextVal--
}
}
})
}
}
function * createCountdown (from) {
for (let i = from; i >= 0; i--) {
yield i
}
}
Equivalent code using generators
const iterableIterator = {
next () {
return { done: false, value: 'hello' }
},
[Symbol.iterator] () {
return this
}
}
Iterator protocol
Iterable protocol
const asyncIterator = {
async next () {
return {
done: false,
value: "someValue"
}
}
}
const asyncIterable = {
[Symbol.asyncIterator] () {
return {
// iterator
async next () {
return {
done: false,
value: "someValue"
}
}
}
}
}
import { setTimeout } from 'node:timers/promises'
async function * createAsyncCountdown (from, delay = 1000) {
for (let i = from; i >= 0; i--) {
await setTimeout(delay)
yield i
}
}
const countdown = createAsyncCountdown(3)
for await (const value of countdown) {
console.log(value)
}
import { createReadStream } from 'node:fs'
const readable = createReadStream(
'logs.jsonl',
{ encoding: 'utf-8' }
)
// a readable stream is an Async Iterable!
for await (const chunk of readable) {
// do something with a chunk of data
}
import { createReadStream } from 'node:fs'
const readable = createReadStream(
'logs.jsonl',
{ encoding: 'utf-8' }
)
// a readable stream is an Async Iterable!
for await (const chunk of readable) {
// do something with a chunk of data
}
A chunk is an arbitrary amount of data (not necessarily a line)
// utils/byline.js
export async function * byLine (asyncIterable) {
let remainder = ''
for await (const chunk of asyncIterable) {
const lines = (remainder + chunk).split(/\n+/)
remainder = lines.pop()
yield * lines
}
if (remainder.length > 0) {
yield remainder
}
}
import { createReadStream } from 'node:fs'
import { byLine } from './utils/byline.js'
const readable = createReadStream('logs.jsonl', { encoding: 'utf-8' })
const errorsByCustomer = {}
for await (const line of byLine(readable)) {
const message = line === '' ? {} : JSON.parse(line)
if (message.error === 'ERR_SYS_FCKD') {
if (!errorsByCustomer[message.customerId]) {
errorsByCustomer[message.customerId] = 0
}
errorsByCustomer[message.customerId]++
}
}
console.log('Errors by customer:')
console.log(errorsByCustomer)
import { createReadStream } from 'node:fs'
import { byLine } from './utils/byline.js'
const readable = createReadStream('logs.jsonl', { encoding: 'utf-8' })
const errorsByCustomer = {}
for await (const line of byLine(readable)) {
const message = line === '' ? {} : JSON.parse(line)
if (message.error === 'ERR_SYS_FCKD') {
if (!errorsByCustomer[message.customerId]) {
errorsByCustomer[message.customerId] = 0
}
errorsByCustomer[message.customerId]++
}
}
console.log('Errors by customer:')
console.log(errorsByCustomer)
Original Front cover photo by Jörg Angeli on Unsplash
Original Background photo by Michael Behrens on Unsplash