Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

A primer on predicates for LogUI

By: hoakley
3 April 2025 at 14:30

All good log browsers provide tools to narrow down the log entries they display. Without those, it would be easy to waste all day wandering through tens of thousands of entries. One common tool provided by macOS, directly and in the log command tool, is filtering using predicates. Although LogUI provides easy access to simple predicates, to get the best from them, it’s worth digging a little deeper, as I do here.

Instant predicates

LogUI’s instant predicates filter log entries according to any of four basic predicate types:

  • subsystem, such as com.apple.sharing, the field shown in yellow in log extracts;
  • eventMessage, the text message listed in white/black at the end of each entry;
  • processImagePath, such as mediaanalysisd, shown in blue, the name of the process making that entry;
  • senderImagePath, such as libxpc.dylib, shown in red, the name of the process sending that entry.

These are quick to enter in the text box to the right of the popup menu in the window’s toolbar, but in many circumstances can prove too broad, and need narrowing down further. In other situations, you want to browse entries from two subsystems, or using a combination of criteria. The best way to do that is to write a short predicate. For single use, you can do that in the one-off predicate editor using the Set button.

When you want to reuse that, you can add it to the predicate popup menu using Settings Predicate (currently a bit kludgy).

Predicates

macOS can use predicates in other situations, most commonly for Spotlight search. If you’re interested in those, see Apple’s Predicate Programming Guide. Here I’ll describe predicates as they’re more commonly used to filter log entries, as they’re usually much simpler.

Each simple predicate consist of three parts:

  • the name of one of the fields in a log entry, such as subsystem or eventMessage. This sets where the filter looks in each entry;
  • an operator, which might be == for ‘equals’ exactly, or for text is commonly CONTAINS[c] for case-insensitive contains;
  • text or a numeric value to look for, such as “error” or 513. Only those entries equalling or containing (or whatever the operator means) this in the specified field will then be returned from the log and displayed.

Here are some basic examples.

eventMessage CONTAINS[c] "error"
entries will only be those with the text error in their message field.

subsystem == "com.apple.duetactivityscheduler"
entries will all have that text, ignoring case, but only that text, as the name of their subsystem.

subsystem CONTAINS[c] "com.apple.xpc"
entries will have any subsystem containing that text, which also includes com.apple.xpc.activity.

Fields

Although you can use any of the fields shown in LogUI (and some that aren’t), the most commonly used are, in order as they are shown in LogUI’s window:

  • eventType (red) – matches the type of event, such as logEvent (1024), traceEvent (768), activityCreateEvent (513), or activityTransitionEvent (514). Can be given as characters (case-sensitive) without quotation marks, or using the digits given in parentheses. Use these only with the operators == or !=, as they are treated as numbers rather than text.
  • category (green) – this matches the category, and varies according to subsystem. This is given as text in quotation marks, and is normally lower-case.
  • messageType (white/black) – matches the type of message for logEvent and traceEvent, and includes default (0), release (0), info (1), debug (2), error (16), and fault (17). Can be given as characters (case-sensitive) without quotation marks, or digits as shown in parentheses. Use these only with the operators == or !=, as they are treated as numbers rather than text.
  • senderImagePath (red) – this matches the text pattern in the name of the sender, which might be the name of a library, extension, or executable.
  • processImagePath (blue) – this matches the text pattern in the name of the process that originated the event.
  • subsystem (yellow) – this matches the subsystem specifier, e.g. com.apple.TimeMachine, given as text in quotation marks. You may find it best to use CONTAINS[c] rather than ==, to allow for differences in case and extended subsystem specifiers.
  • eventMessage (white/black) – for this, you specify a text pattern, or text, within the message, given as text in quotation marks.

Operators

The following comparisons and other operators are available:

  • == (two equals signs) for equality
  • != or <> for inequality
  • >= or => for greater than or equal to
  • <= or =< for less than or equal to
  • > for greater than
  • < for less than
  • AND or && for logical and
  • OR or || for logical or
  • NOT or ! for logical not
  • BEGINSWITH, CONTAINS, ENDSWITH, LIKE, MATCHES for string comparisons, using regex expressions when desired; strings can be compared with case insensitivity and diacritic insensitivity by appending [cd] to the operator, e.g. CONTAINS[c] means case-insensitive comparison
  • FALSE, TRUE, NULL have their expected literal meanings.

There are others as well, but you’ll seldom use them to filter log entries.

Building complex predicates

To see the scheduling and dispatch of background activities by DAS-CTS, you need to look at log extracts showing both their entries. Use the predicate
subsystem == "com.apple.duetactivityscheduler" OR subsystem CONTAINS "com.apple.xpc"
to do that. The first part of it includes those entries from DAS, and the second includes those for XPC and its relatives that run CTS. Using an OR between the two parts combines both sets of entries in the one extract.

To see the reports posted by XProtect Remediator, you need to look at those entries made by its subsystem that have the right category, using the predicate
subsystem == "com.apple.XProtectFramework.PluginAPI" AND category == "XPEvent.structured"
Using the AND operator ensures that the only entries shown come from that one subsystem, and they are given just that category.

Time Machine involves a combination of different subsystems and messages. To get a good overview of relevant entries, you can use
subsystem == "com.apple.TimeMachine" OR
(subsystem == "com.apple.duetactivityscheduler" AND eventMessage CONTAINS[c] "Rescoring all") OR
(subsystem == "com.apple.xpc.activity" AND eventMessage CONTAINS[c] "com.apple.backupd-auto") OR
eventMessage CONTAINS[c] "backup" OR
eventMessage CONTAINS[c] "Time Machine" OR eventMessage CONTAINS[c] "TimeMachine"

I’ve broken this down into separate lines, but you shouldn’t do that in the predicate. Taking it line by line it becomes simpler to understand. Use parentheses () to group each part of the predicate carefully as shown.

You can see other examples in the Help book for my free utility Mints: the Further Information pages towards the end give each of the predicates that Mints uses for its log extracts.

Quick summary

  • [field name] [operator] [text or numeric value]
  • common field names: senderImagePath, processImagePath, subsystem, eventMessage
  • common operators: ==, CONTAINS[c]
  • filter info: “text”
  • combine filters using AND, OR.

LogUI build 37 now has more power for browsing the log

By: hoakley
1 April 2025 at 14:30

By anyone’s standards, the macOS log contains a great many entries, and being able to filter out the noise is essential. This is accomplished by applying predicates to determine which entries are extracted and shown in a log browser like LogUI. However, using predicates requires knowledge about the log and its entries, and forms the greatest barrier for most users. This new version of LogUI improves features to help you use predicates to make the log more accessible.

This all happens in the toolbar of its browser window.

The section at the left of the lower row of tools now provides two methods to apply your own predicates: a one-off predicate editor, and an editor for custom entries in its popup menu.

One-off predicates

Click on the Set button to open the one-off predicate editor.

Here you can compose and paste in your own custom predicates that will extract only the log entries that you’re interested in. In this example, only entries whose subsystem is com.apple.duetactivityscheduler, or contains com.apple.xpc, will be gathered and displayed. Those tell you what’s going on with DAS and CTS scheduling and dispatch of background activities.

LogUI keeps that one-off predicate, even after a restart, as it’s automatically written to its preference file.

Once you’ve clicked Save, selecting the [ … ] item in the predicate menu will apply that predicate to each log extract you obtain.

There’s also an additional standard predicate using the senderImagePath.

Custom menu predicates

Predicates listed in that menu below blowhole are custom predicates saved to LogUI’s preferences using its new Predicate tab in its Settings. This editor is very basic at the moment, and its use a little awkward. This is because SwiftUI much prefers menu contents to be static, so adding items to the predicate menu doesn’t go down too well. This editor allows you to add one predicate at a time, in plain text format.

Click on the Append button here and there’ll be a new predicate named XProtect Remediator with the predicate shown. You can only add one new predicate, then need to quit the app before adding another. I’m sorry that’s so laborious, but once you have set up your custom predicates you can return to using LogUI fully.

The Settings General pane now contains a button to Reset Predicates back to their defaults.

Predicates

A basic predicate is composed of a log field name, like subsystem, followed by an operator such as == (equals) or CONTAINS[c] (case-insensitive contains), and a filter term, usually a string like "com.apple.xpc". So the predicate
subsystem CONTAINS[c] "com.apple.xpc"
will return all log entries with their subsystem containing the text com.apple.xpc. You can combine those basic elements into a more selective predicate using combinators such as AND and OR, so
subsystem == "com.apple.duetactivityscheduler" OR subsystem CONTAINS|c] "com.apple.xpc"
returns entries with a subsystem of precisely com.apple.duetactivityscheduler together with those whose subsystem contains the text com.apple.xpc.

Some years ago I wrote a primer here, and you’ll find some useful predicates in the Further Information section in the Help book for Mints. I’ll be writing more here to help you get the best out of LogUI.

There are a couple of oddities with predicates. SwiftUI tends to like using typographic double-quotation marks, but the macOS predicate builder doesn’t accept them as a substitute for straight marks. So LogUI changes all styled marks to straight ones automatically for you, to ensure those shouldn’t cause a problem. However, when it encounters errors it can behave erratically; while I’m trying to make this more robust, I apologise in advance if using a broken predicate upsets LogUI. It’s worth being careful to check your predicates before trying to use them.

LogUI version 1.0 build 37 is now available from here: logui137

My next task is to improve editing and saving predicates to its preferences, to make them accessible as menu customisations.

LogUI log browser build 31 has better filters

By: hoakley
20 March 2025 at 15:30

This week’s new features in my lightweight log browser LogUI tackle two important areas: initial checks to confirm that the app can access the log, and improving the filtering of log entries using predicates.

LogUI has three key requirements:

  • that the Mac is running macOS 14.6 or later, as enforced by macOS;
  • that it’s run from an admin account, as that has the privileges required to access the log;
  • that there are log records it can access in the path /var/db/diagnostics, as without those it hasn’t got anything to work with.

LogUI 1.0 build 31 now contains code to check the latter two, run soon after launch. If either fails, you’ll see an informative alert, and the app will quit when you click to dismiss that.

LogUI now has internal features to support a wide range of filters that can be applied when fetching log entries. These are an essential means of reducing the number of entries displayed, and of focussing your attention on what’s important.

This is reflected in its Settings, which now refer to Text rather than a Subsystem. The window toolbar now has a Predicate popup menu, and its text box is labelled text rather than Subsystem.

This menu offers the following options:

  • none, which applies no filtering and displays all log entries;
  • subsystem, which uses the text entered as the name of the subsystem whose entries are to be displayed, as in the previous builds;
  • eventMessage, which shows only those log entries whose message contains the text entered;
  • processImagePath, which shows only entries whose process name (or path) contains the text entered;
  • [Edit], which in future will open an on-the-fly predicate editor, but currently doesn’t filter;
  • TimeMachineBasic to blowhole, which use set predicates to display log entries for those features. The first two are different levels of detail for Time Machine backups, error finds entries with that word in their message, kernel finds entries with the kernel as their process, and blowhole finds entries made by my command tool for writing entries in the log.

Text entered is not case-sensitive.

Although it’s currently possible to change and extend those, that involves delicate surgery to LogUI’s preferences Property List, and I don’t intend you to hack that just yet. The next features will provide a proper editor in LogUI’s Settings, and the on-the-fly editor accessed through this menu.

Otherwise LogUI should work just the same as the last build. These new features are documented in its Help book, a separate copy of which is supplied in its Zip archive.

LogUI 1.0 build 31 is now available from here: logui131
and I will shortly be giving it an entry in my log browser Product Page, to make it easier to access. I’m also looking at building an auto-update mechanism into it.

Please let me know how you get on with this, and whether it proves useful to you. Enjoy!

Browse your Mac’s log with LogUI

By: hoakley
14 March 2025 at 15:30

If you ever need to understand what’s going on in your Mac, reading its log is essential. However much you might stare at Activity Monitor, read source code or disassemble components of macOS, what you can see in the log tells you what has really happened. Whether it’s a bug or an unexpected event, it’s the only way to look back and discover what went on.

For most, Console isn’t the right tool. It only offers the options of viewing its live stream, or making an archive of the whole log and wading through that when you need to look at an event in the past. Although my log browser Ulbow gives much better access, for many it’s still a daunting task. I’ve now switched almost entirely to using my new lightweight log browser, LogUI, and here explain how you can use it. Although it’s currently an early release with limited features, you should find it ideal for getting started.

LogUI 1.0 build 27 is available from here: logui127

This comes as a Zip archive, so unZip it, and move the LogUI app to another folder before running it for the first time; your main Applications folder is fine, and almost anywhere will do nicely. Alongside it is a copy of the PDF Help file that’s also inside the app, so you can refer to it when you’re not running LogUI. As the two are identical, you can trash the separate copy if you’re happy to use that inside the app.

Before opening LogUI, generate an event that you can examine in the log. One starter might be launching an app. Try to do this when your Mac is otherwise quiet and unoccupied: if Activity Monitor shows it’s busy with lots of background tasks, then the log could be filling with noise, when what you want most is peace. For the sake of simplicity, time this on an even minute, and make a mental note of that time to the second.

As soon as you’ve done that, open LogUI and you’ll be greeted by its window, with its toolbar ready for you to set up and get your first log extract. That should be set as follows:

  • Start to the current date, hour and minutes. As those are set to the time you open the window, they should already be close to the time of the event. Just after the stepper control is the seconds setting. If the event occurred a moment after the seconds changed to 00, that makes a convenient time to start your log extract.
  • Period set to around 5 seconds. This value can be floating point decimal, so might be 2.5 seconds for a smaller log extract.
  • Max entries set to 1000 to start with.
  • Show Signposts not ticked.
  • Full Fields ticked.
  • Subsystem empty.

Then click on Get Log to see the log extract for that period.

In my case, a five second period overfilled the 1000 I had set in Max entries. Scrolling to the foot of the extract showed that had been reached in less than two seconds, so I increased Max entries to 10000 and clicked Get Log again.

There are now over 5,000 entries in the extract, but they scroll smoothly enough to let me look around them.

The best way of reducing the number of entries to make them more understandable is to add a Subsystem as a filter. In this case, as I’ve launched an app, I can get most useful information from LaunchServices, by entering
com.apple.launchservices
into Subsystem. I also reduce the number of fields shown by unticking the Full Fields box, letting me concentrate on the messages. You can toggle Full Fields without having to get log entries again, as it only affects the fields within those entries that are shown in the window. Then click on Get Log again.

Scrolling down through the 358 entries remaining, I quickly reach one with a message field reading
LAUNCH: translocate to <private> from <private>
Although we could do without the privacy censorship here, it’s easy to work out that the app I just launched underwent app translocation, and that’s confirmed a little further down when LaunchServices gives its full path.

If you want a copy of the text from one or more selected entries, use the Copy command (Command-C), then paste it into any text editor. That doesn’t copy all the fields, only those you’re most likely to need elsewhere. For a full copy of that log extract, with colours indicating the fields, click on the Save as RTF button. As with LogUI’s window title, saved files are given default file names containing the start time of that log extract to help you keep them in good order.

This is the same log extract seen in my rich text editor DelightEd.

Finally, open LogUI’s Settings to enter the defaults you want its windows to open with. These match controls in its toolbar. The last of those, Light Mode, enables you to run the whole app in Light Mode if you prefer. For that to work, your Mac will also need to be in Light Mode, and you should tick that box, close the Settings window and quit the app. When you next open it, it should operate in Light Mode. Other changes made to Settings don’t need you to quit the app for them to take effect.

Enjoy!

LogUI build 25 replaced by build 27

By: hoakley
5 March 2025 at 02:26

I have just replaced LogUI 1.0 build 25 with build 27. This:

  • completes support for Signposts by including them in RTF files,
  • changes the Settings dialog to use Full Fields for consistency,
  • updates technical info with a link to original source code.

You can now download build 27 from here: logui127
or from the link in the original article.

LogUI build 25 can select and copy log entries, and more

By: hoakley
4 March 2025 at 15:30

Last week I introduced my new prototype log browser, LogUI, which seems to have been popular with many. As I now use it in preference to its predecessor Ulbow, I’ve spent a little time adding some new and improved features to bring you version 1.0 build 25. Changes include:

  • support for discontinuous selection of log entries,
  • support for copying text from selected log entries,
  • subsystem names are now case-insensitive,
  • support for Signposts,
  • window names change to include the start time of each log excerpt,
  • RTF saved file names change to reflect the start of each log excerpt.

Settings

These now let you set app defaults for displaying full log entries, and for fetching and displaying Signposts.

Browser controls

The only addition to these is the option to Show Signposts. When that’s ticked, Get Log also fetches all Signposts during the set period, and displays them inline with regular log entries.

LogUI now supports all types of log entry:

  • regular log entries,
  • Activities, events such as clicks/taps and others,
  • Boundaries, markers such as the start of the boot process,
  • Signposts, used to record significant steps and assess performance.

Signposts have their own custom fields, including signpost ID, name, and type, which are displayed when Full Fields are enabled. The only caution with Signposts is that they can outnumber regular log entries, so if you don’t need to see them, it’s better to leave them turned off.

I’m grateful to Joe for asking for the subsystem to be case-insensitive. This means that you can enter com.apple.TimeMachine or com.apple.timemachine as the subsystem and LogUI will display entries with a subsystem name of com.apple.TimeMachine for both. No longer will case trip you up.

Log entries

The biggest changes are in the selection and copying of log entries. You can now select log entries in a browser window. Selections can be multiple continuous using the Shift key modifier, and discontinuous using the Command key modifier. When one or more entries have been selected, you can then copy their text contents using the Copy command or Command-C. Copied text can then be pasted into an app that supports handling of text items in the Clipboard.

Because there are many different fields possible in each entry, copied text consists of a standard set:
date level sender process subsystem message
each separated by a Tab character.

If you want more fields with colour, save the log excerpt in RTF, open it in an RTF editor and copy from that.

If you’re a developer and are wondering how I have implemented this copy feature for a SwiftUI List, let me know and I’ll explain how I managed to pin this tail on the donkey while I was blindfolded, or how persistent guessing overcame the absence of documentation or example code.

Naming

To distinguish between windows and saved RTF files, LogUI now automatically names and renames its windows and the default file names suggested when saving files. Names are based on the Start date and time of the current log excerpt in that window. To begin with, when there’s no log extract, each new window is named LogUI. When it gains its first extract, the date and time are appended to that, e.g. LogUI 2025_03_03_08-14-00, and a similar default file name is offered. When you obtain a new log excerpt in the same window, those names are updated to reflect the changed Start date and time.

Help book

This has been updated to include all these changes.

LogUI 1.0 build 27 is now available from here: logui127
It still requires a minimum macOS version of 14.6, I’m afraid, because of the SwiftUI features it has to rely on.

Enjoy!

Postscript

I have replaced build 25 with 27. This completes support for Signposts, by including them in saved RTF files. I’ve also taken the opportunity to make a small correction in the Settings dialog, and to add a link to the technical info to the log access source code.

Introducing LogUI: an experimental log browser

By: hoakley
26 February 2025 at 15:30

Although I often use my free log browser Ulbow daily, and it serves its purpose well, it’s time to move on with changing macOS and its APIs, and do better. Ulbow still relies on the log command tool to get its log extracts, and its front end is thoroughly AppKit.

Apple introduced the Unified log in macOS Sierra back in 2016, and at that time the only way to access it was using the log command tool, which isn’t among those for which it has provided source code. It wasn’t until Catalina in 2019 that Apple provided an API allowing developers to obtain log entries direct. As that wasn’t retro-fitted, the few apps that access the log couldn’t use that on Macs running Mojave or earlier, limiting its usefulness until relatively recently. When I developed Consolation in 2017, and its successor Ulbow two years later in 2019, I therefore continued to rely on the log command tool to obtain log extracts.

By a curious coincidence, SwiftUI was also first released for macOS in 2019, although it has taken an extraordinarily long time to approach maturity. Over the last couple of years it has improved to the point where some outstanding apps like OmniFocus have now abandoned the older AppKit API in favour of SwiftUI.

Last summer I had a first go at writing a potential replacement for Ulbow using the combination of OSLog to give direct access to log entries, and SwiftUI for its interface. At that time I reached an impasse largely as a result of excessive memory use and a memory leak that I was unable to resolve, and concluded that “Ulbow already performs better than LogUI ever could.”

More recently I have returned to that project and accepted that trying to support older versions of macOS before Sonoma is too great an impediment. Calling on some newer features in SwiftUI I have made better progress and, although not entirely continent in its use of memory, I now have an experimental version that I’m using daily. It appears robust and stable, and shouldn’t run away with all your Mac’s memory. Although it currently lacks key features like Find/Search and the ability to copy text from its browser window, it can store log extracts in rich text format.

Its Settings establish defaults common to all new browser windows:

  • Subsystem allows you to filter entries by a single predicate for one specified subsystem.
  • Period lets you set a default period for log excerpts, given in decimal seconds, so you can set 2.5 seconds if you wish.
  • Max entries is the limit of entries to be fetched and displayed. This can be set as high as 20,000 or even more.
  • Light Mode will set the app’s windows to Light Mode when you next open the app, if your Mac is also set to run in Light Mode. This allows you to opt out of Dark Mode if you really must.

Window controls let you override the first three of those defaults, and add

  • Start, a date and time to start the log excerpt.
  • Full Fields, whether to show all fields or just a shortened selection.
  • Get Log button to get a log excerpt.
  • Save as RTF button to write the log excerpt out to a rich text file.
  • The count of entries in the current excerpt.

The end result is, I think, a considerable improvement on Ulbow, in terms of readability, without sacrificing too much space as in Console’s rigid columns.

This experimental release comes with its own PDF documentation, also accessible within the app as its Help book. I’d be very grateful if you could take it out for a spin and see how robust its log engine is, and whether you prefer its new layout for log entries. You can download a copy from here: logui120
Note that it requires a minimum macOS version of 14.6, to ensure that it doesn’t rob your Mac of memory.

Once its engine is reliable, I will add more features, starting with Search/Find support, and the ability to copy entries from its window.

Thank you, and happy logging around.

误入 Autumn leaves 音乐现场的观众(C调)vlog.74

By: Steven
24 February 2025 at 20:07

【曲目】Autumn leaves

【场地】一方音乐·深圳龙岗

【鼓手】阿吉

【吉他】筱烨、秋夏

【键盘】菜菜

【贝斯】苏志斌

点击这里跳转到 BiliBli 观看:https://www.bilibili.com/video/BV1ZYAReSEv4

点击这里跳转到 Youtube 观看:https://youtu.be/W5ytb1LRvhM

How to deal with a kernel panic

By: hoakley
19 February 2025 at 15:30

A kernel panic is completely different from an app crash. When apps suddenly vanish because of a bug or error, everything else should carry on as normal. When your Mac restarts of its own accord, or you find it shut down and have to start it up again, that’s almost certainly a kernel panic, and should be taken seriously.

Kernel panics occur when macOS can’t continue running any more due to severe (software) damage, and its only option is to restart itself in order to resume normal services. When this happens before the login window appears during startup, it normally results in a boot loop, as discussed elsewhere. This article covers the more common situation when a Mac restarts spontaneously and completes login without any further panic occurring.

Virtual machines (VMs) can also suffer kernel panics, as well as causing the host macOS to panic. Those should behave like other panics, although if you’re unlucky the VM might be damaged and unable to restart properly. If that happens, your only option is to try starting that VM up in Recovery, to see if it can be repaired there. If not, and you can’t get it to start up again, you’ll need to trash it and restore its last backup copy.

Save the panic log

Within a minute or so after restarting, a Panic Alert is displayed, inviting you to agree to send the panic log to Apple. Don’t simply agree to that, though, as it’s the only record of what happened. Panic logs used to be saved in /Library/Logs/DiagnosticReports, from where you could open them in Console, but more recently may be found somewhere closer to /var/db/PanicReporter. You therefore need to copy its contents as soon as it appears and before sending it to Apple.

If the alert isn’t already showing the panic log, click on its Report… button, then open a text editor like TextEdit. Copy the whole contents of the panic log into an empty text window and save it somewhere safe before clicking on the button to send the report to Apple. Once you’ve done that, the alert is dismissed and can’t be brought back.

It’s commonly assumed that sending a panic log to Apple means that an engineer will look through it and get back to you with some sort of diagnosis. That isn’t what the report does, though: it’s processed automatically and, while there’s nothing stopping someone at Apple contacting you about it, that simply doesn’t happen. Only by saving a copy of the log could you contact Apple Support and ask for their help. Also consider filing a Feedback report containing a description of what happened and your copy of the panic log, particularly if you have clues as to its cause.

Immediate actions

The three most common reasons for kernel panics are:

  • hardware (and device firmware) failure or error,
  • kernel extensions,
  • conflict with a peripheral.

If you suspect a hardware failure, or wish to rule that out, shut your Mac down once you have captured the panic log, disconnect all non-essential peripherals, and start it up in Diagnostics to run its hardware test routines. If you’re not reassured that all is well, don’t hesitate to get your nearest Apple store or authorised service provider to run their more advanced diagnostics as well.

On Apple silicon Macs, Diagnostics is different in relying on a hidden key combination: start the Mac up in Recovery by holding the Power button, and in the initial Options screen, hold Command-D until the Diagnostics Loader starts. This may require download of the disk image from Apple’s servers before testing can proceed, so a good Wi-Fi connection is important. Once loaded, there’s a hidden option for extended diagnostics that can be triggered by holding the Command-E key combination.

Panics associated with peripherals such as Thunderbolt docks and hubs are best diagnosed by running the Mac without the suspect hardware connected, to test if panics continue. If they do, and you remembered to save panic reports, even if you don’t understand their details you can still compare them to see if they look similar.

Kernel extensions

Third-party kernel extensions are normally found in /Library/Extensions or the app using them, from where macOS stages them into a folder in /Library/StagedExtensions where they’re protected by SIP. Most are only loaded on demand, so the mere presence of an extension there isn’t sufficient evidence to convict it of causing the panics. However, you should become suspicious when a third-party extension is named in the panic log as being part of the chain that may have caused it. Most software that used to rely on kernel extensions has now been updated to use system extensions or another modern replacement, so updating old software could solve the problem.

Tools for working with kernel extensions are detailed in the article on boot loops. If you need to remove a kernel extension, this article explains how to do that. One quick way to disable all third-party kernel extensions on an Apple silicon Mac is to start it up in Recovery, go through to the main Recovery window, open Startup Security Utility from the Utilities menu, disable loading kernel extensions and return it to Full Security.

Modern System Extensions don’t run with the same level of privilege as kernel extensions, so in theory shouldn’t be capable of causing kernel panics. However, experience has shown that the macOS kernel extensions required to support them can prove unstable and cause panics. This should be apparent from careful reading of the panic log.

Reading the panic log

Unlike app crash logs, panic logs are normally relatively brief and to the point. Although they may be non-specific and not help you much, in many cases they contain obvious clues as to what caused the panic. Formats have changed over the years, but the following sections are likely to prove worthwhile examining.

Immediate cause

At the very top, following the first word panic, the log may suggest a cause. This is most common when a memory leak is to blame, such as
panic(cpu 8 caller 0xffffff80017729eb): "zalloc: zone map exhausted while allocating from zone kalloc.12288, likely due to memory leak in zone kalloc.48 (6586956000 total bytes, 137228148 elements allocated)"@/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/xnu/xnu-6153.141.1/osfmk/kern/zalloc.c:3627

This first tells you which CPU core the panic occurred on. If you have repeated panics, keep a note of these, as they may cast suspicion on a core with a hardware problem.

In other cases, you may not be as lucky, and the cause is just given as an ‘exception’, or
panic(cpu 0 caller 0xfffffe002f4e48bc): cannot find IOAESAccelerator
panic(cpu 0 caller 0xfffffe001abd5e94): Kernel data abort

so you’ll need to look for other clues. As the name implies, exceptions are conditions requiring special handling by the operating system. They include page faults, in which something has tried to access an invalid memory address, invalid instruction codes for the processor, and general protection faults which include a wide variety of other bugs. As far as the user is concerned, all exceptions indicate a bug or problem in the code that’s being run.

OS details

You may see a line like
OS version: Not set yet
simply indicating that the version hasn’t been recorded yet. When it has, this gives its build number rather than version. Most importantly, you should see a statement of the kernel version running:
Kernel version: Darwin Kernel Version 24.2.0: Fri Dec 6 18:57:59 PST 2024; root:xnu-11215.61.5~2/RELEASE_ARM64_VMAPPLE
You can check that against that shown in Software in System Information.

On Apple silicon Macs, you should also see the iBoot version, and the current level of boot security:
iBoot version: iBoot-11881.61.3
secure boot?: YES

The latter is important, as running in Secure Boot means that no third-party kernel extensions have been loaded.

Memory leak

If there has been a memory leak, the panic log may well contain a breakdown of system memory zones giving more detailed clues.
Zone Name Cur Size Free Size
vm objects 78041088 26795008

Zone Name Cur Size Free Size
kalloc.32 280834048 3040
kalloc.48 6586956000 4896
kalloc.64 4241453056 5000896

Note how the Free Sizes of kalloc.32 and kalloc.48 are very small, and that of kalloc.64 is fairly low too. This is consistent with the kernel running out of memory in one of those zones. Further information may follow:
Backtrace suspected of leaking: (outstanding bytes: 288)
Because there’s the suspicion of memory leakage, the panic log also gives a detailed backtrace of where it suspects that leakage is occurring, and details of the kexts involved in that. Note that those may not coincide with any kexts identified earlier as possible culprits.

Panicked task

This may simply be the kernel
Panicked task 0xfffffe166cff1f18: 10735 pages, 374 threads: pid 0: kernel_task
or may give more specific information
BSD process name corresponding to current thread: WindowServer
Boot args: chunklist-security-epoch=0 -chunklist-no-rev2-dev

or
Panicked task 0xfffffe1b55369798: 24964 pages, 8 threads: pid 800: com.apple.Mobile
This is the name of the process running its code at the time, and can be another clue as to where the problem lies.

You may also be given a list of kernel extensions that might be involved:
Kernel Extensions in backtrace:
com.apple.filesystems.apfs(1412.141.1)[6DA33D13-4501-3D48-B4D8-0329E6AEC86D]@0xffffff7f84e7d000->0xffffff7f84fa4fff
dependency: com.apple.kec.corecrypto(1.0)[804DD660-F561-3444-A076-05D7A52D65E3]@0xffffff7f82746000

Third-party kexts

Whatever the cause, you should next look at the list of unloaded and loaded kexts forming the rest of the panic log. These are listed in the order that they were loaded, with the most recent kext at the top. As third-party kexts are the last to be loaded, the top of the lists start with any third-party kexts installed on that system and loaded at the time of the panic.
last loaded kext at 939128480512562: >!UAudio 323.4 (addr 0xffffff7f86baa000, size 434176)
last unloaded kext at 948795488738566: >usb.IOUSBHostHIDDevice 1.2 (addr 0xffffff7f8556c000, size 45056)
loaded kexts:
>!ATopCaseHIDEventDriver 3430.1

In most cases, the name of the kext as you’ll find it in /System/Library/Extensions is the last part of the ID given. For example, the kext with the ID of com.apple.driver.AppleMobileFileIntegrity is named AppleMobileFileIntegrity.kext.

If those lists contain any third-party kexts, they should be immediately suspected as being the cause of that panic, unless another cause is apparent.

Summary

  • Save the panic log before sending it to Apple.
  • Consider running Diagnostics if there’s the possibility of a hardware problem.
  • Consider disconnecting a peripheral if that could be the cause.
  • Consider removing/updating any third-party kernel extensions, or better, disabling them altogether.
  • Read the panic log to provisionally identify its most likely cause, and try to address that.
  • Report the panic to Apple Support and/or via Feedback.
  • Keep a careful watch for any further panics, and be prepared to revise your provisional diagnosis.

Please don’t delete your logs

By: hoakley
28 January 2025 at 15:30

Several of my most popular apps make use of the main system log in macOS, and I occasionally get questions from those who can’t get them to work properly because their Mac’s log files have mysteriously disappeared. This has puzzled me for the last few years, but I think I’ve discovered the cause: some apps delete those log files in a misguided attempt to clean up your Mac and save storage space. This article explains why you shouldn’t do that.

Which logs?

In theory, since macOS Sierra introduced the macOS Unified log, there should be only one log, and one folder containing its log files, hidden away in /var/db/diagnostics.

In practice, many apps and even parts of macOS ignore those and write their own log files. Those can appear almost anywhere, but popular locations include:

  • ~/Library/Logs, the worst offender;
  • /Library/Logs, which should be better maintained;
  • /var/log, normally properly maintained;
  • /var/logs, small and properly maintained.

Those locations contain a mixture of ephemeral information of little value, and some potentially important records, such as the outcome of all volume checks made by fsck on APFS and HFS+ volumes. If your Mac was migrated from a previous model, that may well have copied across logs going back years, and those four folders could well contain dozens of files, and consume several GB of space on your Mac’s startup disk.

What maintains them?

While the Unified log has a dedicated service, logd, that is constantly maintaining the files in /var/db/diagnostics, there’s no app or service that maintains all the others. As they’re considered to be custom features outside the control of the main log, it’s up to the apps that write them to remove old entries when they’re no longer required. Routine system housekeeping used to be performed at regular intervals, run by periodic scripts, but even those have been removed from macOS Sequoia, and before that much of the responsibility still rested with apps that create their own logs.

Logs in /var/log and /var/logs should be well maintained, but for most of logs written outside the Unified log, the answer is that apps responsible for maintaining their own logs don’t, and leave it to the user to clean up after them. Neither do those apps warn the user that they write their own logs but don’t maintain them.

Unified log

Look down through the list of active processes in Activity Monitor and you’ll see two, logd and logd_helper, that are always present from shortly after a Mac starts up until it shuts down. Unlike traditional Unix logs, the Unified log is maintained so its size remains fairly constant, with the main folder of log files, Persist, taking up a little more than 500 MB, and the whole of /var/db/diagnostics coming to about 1.5 GB.

logd does this by working through all the individual entries in the log files and removing those that have passed their retention time. It thus preserves more important entries for longer, and prevents the logs from growing uncontrollably. The unfortunate side-effect of this is that Macs that are making copious log entries have logs that don’t go back long in time. At its worst, that might mean that they may only last a few hours before logd removes them.

Although the Console app provides limited features for accessing your Mac’s log, and the great majority don’t want to go near it, the Unified log has important contents. Among those are reports by Time Machine on its recent backups, and those from XProtect Remediator on its scans to detect and remove malware. It’s also the place where all significant errors and failures should be recorded. Remove the main log files and those all disappear. Apple also may ask for and use recent log records when investigating problems you report to Apple Support or, for developers, using Feedback. Even though you may not want to browse the log yourself, it’s important and not a feature you should throw away.

Housekeeping

Spending a little time every month or so doing the housekeeping on your Mac is an excellent and rewarding investment of your time. When I do this, I like to preserve those logs that might still be significant, including the inevitably copious files written by Adobe’s multitude of services, and anything that still appears to be active. But those that haven’t been modified for the last year or so should be disposable, and any from apps I no longer use can be cleared away without fear. However, I never interfere with the Unified log in /var/db/diagnostics as I know that logd cares for that constantly, and much better than I can.

I’m well aware that many feel that they can delegate the responsibility of housekeeping to an app, now typically one they pay a substantial subscription for. Just as I wouldn’t pay someone to come and work through all my personal papers and records, destroying those that they thought I didn’t need, I will never use an app that does the same with my Macs. If the housekeeping app you use deletes log files, particularly if those could include those of the Unified log, then I’d consider that to be malicious, and proof that the app’s developers don’t understand how macOS works.

And that’s why some discover that those vital logs have been destroyed, and they can’t run the checks built into several of my apps, including the records of XProtect Remediator’s security scans.

Recommendations

  • Users: perform housekeeping as a routine, particularly on disused and unnecessary logs in ~/Library/Logs. Never interfere with logs stored in /var/db/diagnostics.
  • Developers: avoid writing your own logs. If your software really has to, ensure that it performs its own housekeeping, and inform the user in its documentation.
  • Apple: if the Unified log can’t be unified, improve it and make it fit for purpose.

在 github 架设 hugo blog(纯浏览器操作)

By: fivestone
11 November 2024 at 21:12

我其实对 Hugo 不熟,不知道这算不算重新发明了一遍轮子。但我搜索「如何在 github 上,用 hugo 架设自己的 blog?」时,搜到的教程,都需要用户在自己的电脑上,安装运行各种 git 和 hugo 的相关命令,感觉对新手并不友好。所以,我试着写了一个流程,让新人完全只需要在网页浏览器上操作,就能快速生成自己的 blog 网站。

所有操作都在 Github 这个项目上进行:
https://github.com/fivestone/hugo-papermod-beginning

这个项目本质上,就是搭了一个空白的 hugo 网站,让用户 fork 到自己的账户下,设置一下就能直接使用。对功能和界面有什么额外要求的话,请自行学习 hugo 的进阶教程。——然后你们就不是需要用这个项目的新人啦~

  • 本项目基于 hugo 博客引擎,和流行的 PaperMod 主题
  • 在 github 上建立的 blog,在墙内是不能直接访问的,需注意

1. 创建 github 账号

首先,注册自己的 github 账号,过程略。注册过程中,你设置的账户名 username,通常就是最终的网站地址 username.github.io,当然以后也可以把自己的域名映射到上面。

2. 架设自己的 blog 项目

注意,这一步,有两种方法

  • 第一种方法:你建一个全新的项目,下载我提供的 .zip 文件,解压后,再手动上传到你的项目。和第二种相比,稍微繁琐一点。但还是希望大家,有条件的话,使用这种方法。
  • 第二种方法,把我的这个项目,fork 到你的项目。这种方法对新人更简便,完全不需要在本地操作文件,只用手机或 pad 就可以完成。但这种 fork 在一起的项目,在进行自动发布 blog 的操作时,是共享同一个操作额度的。如果 fork 的人数非常多,未来可能会被 Github 限制。——要达到这种规模,大概要几千人同时用吧……所以也不需要很在意。
2.1. 第一种方法

注册并登入账号后,新建自己的项目(Repository)。

项目的名称,决定了最终 blog 的网址。假设你的 github 用户名为 username

  • 如果把项目命名为 username.github.io ,则最终的网站地址为
    https://username.github.io/
  • 如果把项目设置成其它名字,如 new-name,则最终的网站地址为
    https://username.github.io/new-name

确认项目为 Public。其它设置都不需要更改,点击绿色按钮创建。

创建项目后,点击「上传已有的文件」

我的 github 项目,下载已经设置好的 hugo 文件包,在本地解压缩 .zip 文件。然后,把里面的所有文件,拖拽上传到你的项目里。

等到 80 多个文件都被上传后,别忘了点击页面底部的 Commit changes 提交。

2.2. 第二种方法

注册并登入账号后,进入项目:
https://github.com/fivestone/hugo-papermod-beginning
点击 Fork,将这个模板复制到你自己的账号下。

和第一种方法一样,这里需要设置你自己的项目名称,假设你的 github 用户名为 username

  • 如果把项目命名为 username.github.io ,则最终的网站地址为
    https://username.github.io/
  • 如果把项目设置成其它名字,如 new-name,则最终的网站地址为
    https://username.github.io/new-name

然后点击 Create fork 创建项目。


3. 配置自动发布 blog

创建新项目后,进入项目的 Settings – Pages 页面,把 Build and deployment – Source,改为 GitHub Actions。

把 Source 从 Deploy from a branch,改为 GitHub Actions 后,进入上方的 Actions 页面。

初次进入 Actions 页面后,会显示 Github 预设的各种配置方案,通过搜索框找到 hugo,然后点击 hugo 方案中的 Configure

系统会自动生成配置文件,不需要做任何改动,点击绿色的 Commit changes 提交。

此时自动发布的 action / workflow 就已经开始运行了,大约 1~2 分钟后,就可以在
https://username.github.io 看到 blog 最初的页面了。

以后,每次对项目里文章或配置文件的更改,都会触发这个 action / workflow,重新生成一遍网站。可以在 Actions 页面,查看 workflow 每次运行的情况。


4. 更改网站基本信息

在 Code 页面,点击编辑 config.yml 页面,把一些预设的网站信息,改成你自己的信息。

对新人来说,需要在 config.yml 文件里更改的,大概有以下几项:

baseURL: https://username.github.io/ # 改成你自己的网址
title: 网站名称
params:
  author: somebody # 作者的署名

  homeInfoParams:
    Title: 网站标题,只显示在首页上
    Content: >
      显示在首页标题下方的一些文字。</br>
      支持一些简单的 html 和 markdown。

更改后,点击绿色的 Commit Changes… 在弹出的页面中,再一次点击绿色的 Commit Changes,保存文件后 1~2 分钟,就可以在 blog 页面上,看到更改后的内容了。


5. 添加、管理文章

所有的 blog 文章,都在 content / posts 目录中。在 Code 页面,进入 content / posts 目录。点击右上角,创建新文件。

所有的文章,均为 .md 结尾的 markdown 文件。文件名对应着这篇文章的网址,譬如,post-20241111.md 文章链接,就是
https://username.github.io/posts/post-20241111/

在文件的开头,如图所示,写入用 — 隔开的,文章的标题和发布日期。

---
title: "新文章的标题"
date: "2024-11-11"
---
然后开始写正文,markdown 格式。

同样,点击绿色的 Commit changes… 保存提交。1~2 分钟后,就可以在 blog 页面上看到新文章了。

content / posts 目录里的所有文件,都可以随意地新增、删除、修改、重命名文件。对应着 blog 文章的增删改、和改变 url 链接。

文章内嵌的图片,建议放在 static 目录下,然后在文章中用 markdown 格式引用。譬如 static / aa.jpg 文件,相应地在 markdown 文件中插入的代码为:

![](https://username.github.io/aa.jpg)

有经验的用户,也可以使用其它更有效的组织方式。


6. 其它注意事项

  • 生成的 blog 对应的 rss 订阅地址为
https://username.github.io/index.xml
  • 如果是用第二种方法,直接 fork 的项目,以后在这个 blog 项目里,会一直看到,图片里这样的消息,提示要把你对项目的更改,反馈给原本的我的项目。——不用理会就是了。

热力图

By: dimlau
6 November 2024 at 14:43

文章更新热力图

类似的热力图,我最早是在 GitHub 看到的,用来展示开发者的更新频率。现在看到不少博客也做了这种小组件:就是从当前日期向前追溯一年,每一天显示成一个灰色的小方块,如果当天有文章发布,就显示成浅绿色,如果当天发布的文章不止一篇,就显示成深绿色。我想了一下,用 Hugo 的模版系统,加上一点 CSS 做布局和显示优化,应该就能实现,动手试了一下,果然效果还算可以。目前可以在首页看到实际演示。

fin.

十三岁的唱片制作人!敏锐的设计洞察力来自街头?设由心声:序 Vol.0

By: Steven
28 July 2023 at 19:05

🎥 点击跳转至 B站 播放视频:https://www.bilibili.com/video/BV1SN411a72T/

🎥 点击跳转至 YouTube 播放视频:https://youtu.be/o3XL_woaCUs

这是新企划的设计师视频系列《设由心声》的序章。在这期视频里,你将会看到设计意识自我觉醒的过程,以及不可遏制的创作冲动是如何生长的。

这是想重启很久的一个全新大坑:设计师访谈 + 产品与设计背后的内幕信息。

这一条是今年三月份去上海参观《拍照的人》线下展的时候,和 akira 在前台做的采访。你将会看到一个设计师自我觉醒的过程,和他的学习方法。

因为原本就有重新开启这个新企划的打算,于是借机做了这一期访谈,作为整个系列的开篇。 这个系列应该都会是这种类型的类似纪录片的形式,来展现设计和产品的业内信息和观察。为什么说是「重启」呢?因为很多年前做过一个设计师访谈的专栏,因为各种原因只做了七期就终止了,现在想用视频的形式复活这个企划。

早年的设计师采访文章见:

输出是一种排泄

By: Steven
20 May 2024 at 21:13

在不同的平台上时不常的都能看到一些内容创作者他们会有疑问,说我的东西明明很有深度,准备得也很充分,制作也很用心,但是为什么没有获得很好的流量,或者其他的回报?这种时候要么就是真的有疑问,要么就是想通过这种疑问的方式,来表达对于这种流量的不满或者鄙视。

每次看到他们说这种话的时候,我就会代入到自己。我也有很多内容是花了很多心思很认真做的,但就是没有什么人看,没有什么人听。前几年确实会有疑惑,但现在我很坦诚地接受自己就是不擅长做那种大众流量欢迎的内容。

这里并没有鄙视大众流量的意思,我是真的发自内心的不懂,哈哈哈哈哈~

因为我做内容 99% 的动机,都只是为了把脑袋里的东西腾出来,它只是我的一个思考过程的外化。有人获得共鸣和启发,那就最好,没有那也无所谓。因此我确实没有真的花过心思在研究怎么样制作大家都喜欢的那种类型的内容,因为我也确实没有发自真心地想把自媒体作为自己的一条所谓职业赛道来看待。

因此,没有获得那样的流量,是很正常,也应该的。

一张封面引发的内核更换

By: Steven
28 April 2024 at 18:29

我把博客的模版换了,更简洁,但更好用了。

事情要从「播客封面的输出事故」开始。

我在播客后台上传的一张 1600×1600 的封面图,在通过 RSS Feed 分别同步到小宇宙和 Apple Podcast 的时候,出现了不显示或被识别成「未提供」的状态。小宇宙的后台能看见,这张图是抓到了的,但在播放页没显示;而在 Apple Podcast Connect 的后台就直接识别为「未提供」。同样的源我也尝试给到 YouTube,能抓到,能显示,但非常模糊。

第一时间我就认为是博客那头的设置问题,但具体是什么原因呢?实在是太多年没有折腾过博客的模版设计了。每个菜单我挨个检查了一遍,发现确实有个「摘录」的开关打开了,它会限制 Feed 分享出去的是完整的一篇还是只有局部的内容。这确实有影响,它导致小宇宙没抓到全文,在单集详情页里只显示了第一段话,后面还跟一个无法点击的跳转的纯文字「阅读更多」。关掉这个开关之后,小宇宙也马上就更新,能显示全文了,但封面依然没有显示出来;Apple Podcast 那边完全没动静,别说更新了,就是搜索都还搜不出来,但明明已经发布了。

我实在想不出是哪里的设置不对,就上即刻问了一下。很幸运的是,小宇宙的小伙伴立刻就开始帮我找原因。在几经周折后,最终联系上了小宇宙的技术同学,他给我看说托管源输出的图片尺寸只有 180×200 px。这就很明确了!

https://suithink.files.wordpress.com/2024/04/vol-0000.jpg?w=180&amp;h=200&amp;crop=1

但我依然不知道为什么,因为翻遍了整个后台,都不存在一个设置 RSS Feed 输出封面尺寸的地方。别的朋友也都没遇到过这样的事。况且,一张正方形的图,就算是缩略图也应该是正方形的,比例变了又是为什么呢?

于是我意识到一件事:

这是一个行业内的标准做法,那就应该是通用的,如果别人的播客都没有这个问题,而博客后台又不存在可设置和调整的界面,那最有可能的原因大概就是,我博客使用的模板太过于老旧了。

为什么我会想到这个角度呢?

因为我博客目前用的模板,是 2013 年开始启用的,这十一年来,只在 2022 年时调整过一次,但技术内核还是原本的那套东西。然而事实上,我博客后台切换到区块编辑器已经好几年了,我还在用的这个老模板其实已经下架很多很多年了,只是因为我一直没有更换它,还在生效而已。

为了验证这件事,我先是研究了一下朋友托管播客的网站结构,确定了「封面图」在通用模版中的形式,再在我的博客后台巡了几圈,选定一些结构相似的、我也喜欢的模板,把它们套用在我的播客日志上,看看是什么表现。最后,我在区块编辑器里找到这些页面,看看它们是怎么表达和处理这张「封面图」的,有哪些可以设置的项。

至此,我锁定,问题的根源就在于,这个多年前就早已下架不再维护的老版本软件的模版,它在技术层面和现行的技术之间的差异,导致输出的封面图变成了一个比例错误的缩略图。我只需要换上一个新模版,就可以解决了。

但「换模版」这件事,其实我已经考虑好长时间了。

在这次「封面图事故」之前,我就有换新的的想法了。一方面确实是,在日常写作和新增一些页面时会明显感觉到这种技术上的代际差,只是自己懒得动,能用就不改。我相信大部分程序员也是这么想的,代码屎山不就是这么回事嘛。但如果只是换个模版,其实不用想那么久,所以另一方面更核心的是,我在同时考虑把博客的套餐升级到 Explorer 版,还想提前买下后面几年的域名使用权,因此,在我心里,模版的更新、升级、域名这三件事是合并在一起考虑的。

这次小事故,倒像是上天推了我一把。

于是乎,我下了决心,升档、域名、模版,一次性全部处理好了。我现在的博客,是一个更为简练、但更好用的全新状态。播客源的抓取也回归正常了。

20132022 到今天,眼看着我的博客越改越简练,但内容越来越充盈,我心中是欢喜的。这就是我这些年的状态,越发充盈,越不需要装饰,所有形式都让位于内容。我只要一件舒适的 T 恤就够了。年轻时喜欢说的个性,那不是通过页面、手机壳、衣裤鞋来体现的,个性是行动做派,不需要是任何视觉化的呈现。

这就是另一种「我变秃了,但更强了」。

在博客上做播客,再因为做播客而全面更新了博客,再把这个过程记录在博客上,我如果不写出来,这事儿说给人听都会觉得我有神经病哈!不过 Blog 和 Podcast 这俩完全不相关的事物,在简体中文里的说法竟然像绕口令一般相似,也是有意思。

兔子王国里的外星人_0.ylog

By: Steven
26 April 2024 at 00:52

欢迎收听荒野楼阁 WildloG,这里是设计师苏志斌的个人播客。作为第 0 期,我会与你分享一下做这个播客的动机、这个播客的主题会是什么、起这个名字的缘由、本期封面和播客 Logo 的设计想法,以及后续的节目计划。

在这一期,你会听到:

—- 我是谁?从我家的动物园,工作和话剧的经历,聊到我二十多年的写作习惯。

—- 我对于视频内容的态度?为什么那么久没更新《设以观复》系列?

—- 原本并不想做播客,为什么转变想法呢?动机是什么?

—- 这个播客的主题:一个外星人

—- 荒野楼阁 WildloG 这个名字是什么意思?因为生机勃勃啊!

—- Why not 和 WildloG

—- 封面设计:隐秘的荒野和兔子王国

—- 以两类节目为主:一个人捡树枝,两人以上一起捡树枝

—- 要停更视频?

|登场人物|

苏志斌:工业设计师,车联网智能硬件产品经理/联创,《设以观复》作者

|相关链接|

若你所使用的播客客户端未能完整显示插图,或遇网络问题未能正常播放,请访问:

荒野楼阁 WildloG 的地址:https://suithink.me/zlink/podcast/

阅读设计相关的各类文章:https://suithink.me/zlink/idea/

|其他社交网络媒体|

苏志斌 @ 知乎|SUiTHiNK @ 即刻 / 微博

苏志斌SUiTHiNK @ Bilibili / YouTube / 小红书

|联络邮箱|

suithink.su@gmail.com

欢迎在 小宇宙、Spotify、YouTube、Apple Podcast 收听本节目,期待你的留言。

💾

分类法

By: fivestone
10 April 2024 at 21:16

把 blog 的文章分类整理一下。

这项任务已经拖延了超过十年,早就没意义了。blog 页面上,已经很多年没有放置分类列表。曾经企图把所有文章归入 uncategorized,然后用 tag 来做分类,——幸好没有,不然又是个半途而废的坑。

还是先把分类的框架搭出来吧。即使有了框架,从前的旧帖子,也还没有妥善归置到各个分类里,继续拖着吧。


yy – /yy

也就是「意淫」,初代互联网口头语之一。某种意义上,整个 blog 的所有文章,都是在 yy,所以从这个网站初建,这就是分类之一。内容偏向于各种奇怪的,未必和情欲相关的幻想。也经常觉得 yy 这个词渐渐不合时宜、甚至有些男性油腻,想换个更高大上的(譬如 P.Bourdieu 的 illusio…);但,还是先这么放着吧。

日子流 – /current

流水账。早年 blog 大兴的时候,一大群人的文章风格,都是今天吃了什么玩了什么买了什么。我也想学着这么接地气一些,但最终还是没写太多。网址的英文名,出自卡佛的《水流》。

nowhere man – /nowhere

旅行相关,或者日常生活中偏向旅行气质的片段。出处是 Beatles。

norwegian wood – /wood

情感相关。出处是村上春树当然也是 Beatles。

不正经的 – /funny

纯搞笑段子。

偶知道 – /seriously

一些比较认真的思考吧。「偶知道」既是偶然知道,也是我知道,——(偶 = 我)仍然是上古网络用语,尴尬到脚抠地。

他们 – /ille

偏向于对他人的描述,有网络引用的,也有实际访谈的。ille 是第三人称自指。关于他者的描述,终归还是要映照自身。

无政府主义审美 – /aesthetic

最初是时政方面的吐槽。后来也包括从无政府主义视角,对同温层内部的一些不同的观感。然而并不会努力地,专注于用这个 blog 向人普及反贼言论,所以最终还是轻轻地立足于「审美」两个字,说些只是看着不爽的东西。

love me do – /tech

和人文情感无关;一些在各种兴趣领域的技术攻略:攀岩、射箭、外语、IT……

拍拍 – /photography

作为童年爱好,这个 blog 曾经把「摄影」作为一个二层分类的顶端,下面包括很多细微的分类:拍的照片、心得、理论讨论……甚至还有过一个摄影 blog,后来停掉了,也没有合并过来。这边还多多少少留了些,就先堆在这里。——所以心中还是对此有爱的吧。要知道,连 IT 类原创都没资格独占一个分类,而是胡乱塞进 /tech /fyi /misc 里面……

FYI – /fyi

For Your Information,感觉会对很多人有用的资源。和 /tech 类似,但兴趣方面的含量就低了很多,只是当作工具来整理,也未必是原创。

misc – /misc

杂项。

把日常阅读的网页,用 RSS 推荐给好友

By: fivestone
8 March 2024 at 22:10

虽然大家写 blog 的频率都没那么勤了,但是,RSS 还是有其它可以玩的方式的!

很多人都有用各种 read it later、或者书签类工具,把有意思的网页保存下来。在这些工具里,可以通过某些手法,把一些想要分享的网页,生成 rss。其他好友订阅这个 rss 地址,就可以自动刷新,看到你推荐的文章啦!

下面介绍一些,常见的书签网站,生成 rss 的方式。但首先——

  • 这些生成的 rss 地址里,大多都内嵌着网站的验证码,容易泄密,也冗长而不简洁。强烈建议:得到 rss 后,先用 Feedburner 之类的网站,转成新的 rss,再分享出来,可以去掉原先网址里的隐私信息。
  • 很多网站,并没有专门用来 share 的分类,只能通过曲线手段,把已经 archive 或者 star 的类别分享出来。这可能会影响你原本的使用习惯。
  • 一些网站生成的 rss,并没有全文,甚至只有标题和链接地址。没关系,能看到大家的推荐,就已经很好了,自己点开就可以啦。
  • 大多数 rss 订阅工具里,也都有发送给 read it later 的功能,可以辗转着,把自己订阅的文章分享给他人。

我的 RSS 分享地址是:

https://feed.fivest.one/readings
或者
https://feeds.feedburner.com/fivestone/readings

有兴趣和我分享的,欢迎留言或私信交换!!


Instapaper 免费版

感觉 Instapaper 生成 RSS 的功能是最好用的,可以把特定的文件夹设为公开,直接得到它的 rss,形如:

https://instapaper.com/folder/1234567/rss/123456/Vciysdfsd7mod9B
  • 在左边栏创建新的 Folder,存放想要分享的文章;
  • 进入这个 Folder,在页面上方选择 Edit,设置成 Public;
  • 点击页面右上角的下拉菜单,选择 Download;
  • 下载为 RSS Feed,就得到 RSS 地址了。

Pocket 免费版

好像 Pocket 的免费用户,内容都只能是公开的(无语…),只要知道了用户名,就可以通过 RSS 查看全部的内容(所以生成的 rss 需要转录才安全)。而且不能自定义分类,只有默认的:

https://getpocket.com/users/USERNAME/feed/all
https://getpocket.com/users/USERNAME/feed/unread
https://getpocket.com/users/USERNAME/feed/read

最后一条 …/read 会返回所有 Archived 了的文章,可以勉强用它作为分类的手段。

Readwise

这个只有收费版,我就不去试了。有它家的用户,可以帮忙把生成 rss 的方法分享一下?

Wallabag

我在用 Wallabag,可以自建,也有收费的服务可用。生成的 rss 是全文输出,效果很好。在 Config – Feed 里,生成一个 token,然后点开任何一个 tag,点击列表上方的 rss 图标,就可以得到这个 tag 的 rss 订阅(生成的地址里带着统一的 token,所以需要转录才安全):

https://wallabag.your.domain/feed/USERNAME/asdfghjkl/tags/t:share

運動無國界,嗎?

By: Steven
21 February 2024 at 21:35

🎥 點擊封面播放視頻

由一個提問開始,我從設計師與創作的角度,談論體育運動中的民族主義,警惕「分類」思維對思考過程的影響。

今日戶外步行運動記錄:開發了一條四分馬的路線!

🎥 B站播放地址:https://www.bilibili.com/video/BV1yv421k7Mg/

🎥 YouTube 播放地址:https://youtu.be/w2KQoic8LAg

這是春節過後的第一條視頻,主要是起個頭跟大家聊聊天。如果你有任何想法,歡迎在視頻中的彈幕或評論區里和其他人一起理性討論。

读完一本不好看的书,但心里很舒坦

By: Steven
13 February 2024 at 19:04

在西西弗里偶遇这本书,随手翻了一下,被设定吸引了,就一下看了前九章。

二十三天后回到书店里把余下的二十二章看完了,满足的同时又觉得很失望。

满足的是,这个下午是我近一年来完整读完了一本书的时刻;失望的是,前半截一直吊着我胃口的摆渡世界的故事,最后居然演变成了俗气的爱情故事和死而复生的怪诞情节。我不喜欢这样的收尾。

但是,迪伦凭着自己的信念从死亡的世界回到人间这段路,这一路的勇气,是我愿意把第三颗星打上来的原因。书里的男女角色我都不怎么喜欢,无辜枉死的三十六岁女士也很莫名其妙,但对于此刻低气压的我而言,我喜欢迪伦一路冲过去的那份勇气和冒险的决心。

对多数人而言,读这本书是浪费时间。但我之所以感觉还行,是因为我太久没有体会到「完成」一件事时「结束」的那一刻了。哪怕这一刻并不欢欣鼓舞,但我完成了。

相对应的,前两天看完的两部片子,让我感到心里非常的舒坦。一个是贾玲的新电影《热辣滚烫YOLO》,另外一个是 Casey 最新的一条 vlog《Sisyphus and the Impossible Dream》。

一方面惊叹于贾玲真的一年瘦下来一百斤,练成了可以和职业拳击运动员打几下的状态;二来佩服于她为了实现这个目标所做的一切努力,一切向生活挥拳而做的事情。她不是瘦了,而是变了一个人,瘦下来只是一个副产品。

Casey 的 vlog 时间跨度长达 17 年。从大腿骨折,到跑进三小时以内,从二十来岁到四十多,一切的付出,就像西西弗斯一次次推石头上山,不仅过程令我震动,结果更是让我感受到了希望!

他俩是我 2024 年初的第一束光。

這夜風正涼 𝄇 就用五首詩與本命年訣別吧!No.73

By: Steven
3 February 2024 at 21:35

我用這條影片回顧了 2023 年裡,對我而言最重要的四件事:

上海三日、父親離世、我的手術、香港四日

但很多感受我沒辦法通過影像或者散文式的語言來表達,它們很複雜,也只有我自己最清楚它們意味著什麼。於是,我寫了五首詩。這些詩都是我的感受和處境。它們或許很難為外人所理解,但如今的我已經清晰地認識到,人和人之間難以真的互相理解,尤其是那些並未參與你人生的人們,因此,並非所有表達都需要被理解,能按照自己的內心,袒露出來,就可以了。對於詩歌而言,我並不追求共鳴。

🎥 播放地址:

B站 https://www.bilibili.com/video/BV1KB421r7xs/

YouTube https://youtu.be/9RnACKm4XhU

這張封面雖然用在了上一篇 blog 中,但其實它本來就是為了給這條視頻做封面,而特意構思出來,在 Midjourney 裡畫的。它所呈現的,是一個被凍在了湖面下的香蕉,一個青澀的香蕉。我站在湖面上,低頭看著湖面,看著它,就像看著自己的處境。

本影片中,亦採用 AI 繪圖工具 Midjourney 製作了 23 張圖像,用於詩和情緒的表達。

第一章:你在橋上看風景

從膠片到數碼,從卡片機到手機,給真實世界切片的權力來到了每一個人手裡。一些人、一些事在和你接觸的過程中,在你的心裡激起了一些漣漪,那一刻的化學反應促使你舉起手機,拍下一些照片和視頻,這存在過但不再存在的一刻,因你而活了下來。

第二章:離去的少年

人與人的關係,是相處出來的。愛、恨、執、怨,皆為充實;唯獨空白,無法驅除。

因緣果報,你無法言說;潺潺流言,你只得不響。

第三章:陌生的天花板

這是你三十六年來第一次住院,也是頭一回做手術。

一棵巨大的樹從天花板上破頂而出,它白色的樹根粗壯有力,厚實的樹根一根根張開,盤踞在一張鋪著綠色床單的、平整的金屬架子床上。你看過一些跟手術相關的畫面,也設計過一些醫療器械,但這個巨大、結實的純白結構直接展現在你的眼前時,你還是受到了一點震撼。醫生、護士、病患,在這個空間里,你只能感受到「問題」和「方案」。性別不是一種凝視關係下的身份,是執行方案過程的所面對的「客觀結構」。

第四章:機翼在反光

朋友們今年都陸續在做出一些轉變:有人結束了設計公司的業務,投身於產品和品牌;有人離開了自己的藝術教育行業,主動擁抱自己的內心;有人帶著相機,一頭扎進了藍色的大海裡。

每人都有一個時機,它到來時,上天會告訴你。

🎥 播放地址:

B站 https://www.bilibili.com/video/BV1KB421r7xs/

YouTube https://youtu.be/9RnACKm4XhU

影片編號:Vlog No.73

博客实现 RSS 订阅能力全记录

By: 李瑞东
11 November 2022 at 21:17

我的个人网站 LRD.IM 从 2018 年元旦发布至今,已经持续维护 4 年了。从最开始根据一个网站模板替换图文,到现在全面的「自主研发」,这是我最骄傲的一款作品。接下来要开始尝试将我的博客内容输出成 RSS 源。

这篇博客不谈设计。写一点自己对互联网的初印象,将博客做成 RSS 源的原因,以及使用 RSSHub 将自己网站生成订阅源的过程。

吹吹水:我的互联网回忆录

对早期互联网的印象

家里 2005 年配置了台电脑,依稀记得那会儿互联网的潮流正好是从门户网站转换到「社会性网络」。

Discuz!、贴吧、QQ空间、开心网等等,都是 Web 2.0 的代表作,我「当年」也有高度参与到这些生态里面。那时候的互联网鼓励人们创造内容,强调互动和内容本身。而不像现在基于所谓的「算法」一直在投喂内容,网站上充斥着营销号、广告、对立、(甚至审查🤫)…

我记得以前网络上的内容是挺有趣的,因为都是自发的内容,感觉很真实。而现在在网上看到「有趣」的内容,却总感觉像是某个 MCN 机构生产出来的,所有内容都有目的,一直在包装、孵化,指向最后的变现…

所以正好我这位在 Web 2.0 时代背景下成长的网民,正好也有掌握了一些专业知识,于是乎就捣鼓出属于我的个人网站 LRD.IM,以及目前为止积累了 40 多篇的设计博客。一方面是想通过这些来积累自己的专业知识,另一方面是更想在互联网上留下自己的(且属于自己)的一些痕迹。

对 RSS 的执念

以前在 WordPress 搭建的博客、新闻资讯等网站总会有一个 RSS 订阅渠道,最初还没有了解过是什么意思,只是记住了有这个很不起眼的橙色的彩虹条纹的图标。

直到在大学了解到了一些信息获取来源方式和分类之后,才真正了解到 RSS 这个东西。很神奇的是它将内容进行重新格式化输出,将内容和从页面中分离出来,供读者在自己喜欢的第三方阅读器里接收新内容并阅读。

我还是挺认同这个概念的,即使它是 20 年前的产物。

分离原本的网页,意味着读者可以根据自己喜好在第三方阅读器上进行阅读,甚至自己捣鼓一个阅读器,而并非局限在原本网页管理者的设计中。

而第三方阅读器通常都会有新内容通知、已读未读标记、分类、标签等功能,这意味着读者能够拥有自己的一套信息获取方式,而不需要受到算法的介入,并能及时获得新内容的推送。

正好由于今年我的设计博客还上了不少公众号、周报的推荐,我察觉到拥有一个自己的内容推送服务迫在眉睫,而 RSS 订阅、邮件订阅都能满足这个需求,正好我就先将 RSS 订阅服务做起来先。

综上所述,我认同 RSS 的中立、纯粹、无算法介入,以及需要获取一个推送功能。所以,这时的我作为互联网内容生产者,是时候为自己的博客搭建一个 RSS 服务了!

实打实:将自己的网站生成 RSS 订阅源

预期

首先阐述下我的预期效果:

  • 抓取到 Blog 页面里的所有博客,并能检测到内容变动(包括增删改);
  • 将抓取的内容生成为 RSS 要求的格式,比如 XML;
  • 更新内容时,在第三方阅读器中确保至少的时效性(不会延迟太久)和内容准确度(顺序不会错乱);
  • 订阅链接用回自己的域名。

尝试一:第三方生成服务

于是乎我首先也试了几个做法,第一个是寄托于第三方的 RSS 生成服务。比如 Feed43FeedburnerRSS.app。当时我是期望这些第三方服务能够满足上述的预期效果,自动抓取我发布的新博客生成为 RSS 文件。它们确实也做到了,但会各自有一些硬伤。

  • Feed43:更新快,准确。但是免费版里生成的 Feed 里面会带有它们品牌的宣传,且国内访问速度一般;
  • Feedburner:更新速度一般,准确。但是被墙了,国内无法正常访问。
  • RSS.app:更新快,准确。但是只能抓取前几条内容,想要保留所有内容需付费升级账号。是否被墙倒是没试过。

试了一遍论坛/讨论组内大伙儿推荐的 RSS 生成服务提供商,没有完美地达到我的期望,每个都是差一点,只能靠自己了。

尝试二:手动制作静态文件

眼见外部服务不好使,于是乎我跑去看了看 RSS 的语法规范,尝试用最笨的方法:按照语法自己手动编写一份 XML 文档出来,给到读者去订阅。

大费周章按 RSS 语法规范写完了 XML,传了两份用作测试,一份传在自己的阿里云轻应用服务器上,另一份传到了阿里云 OSS 里。之后用第三方阅读器订阅 RSS 链接。

结果在阅读器里看到顺序错乱,时间异常,更新非常迟缓。刚开始我还以为这个笨方法能行得通,只是做起来比较麻烦,没想到仍然有这么多缺点。

最后一试:用 RSSHub 将自己网站生成为 RSS 源

了解 RSSHub
前两种方法试过都不能满足我的要求之后,我实在找不到一个好的思路或方向去实现,唯有在 V2EX 上发了个帖子请教下其他人。虽然帖子只有两个人回复,但足够启发我去解决问题了。

一楼回复建议我给 RSSHub 提个申请,让其他人写个规则来抓取我的网站,然后合并到 RSSHub 项目里,用该链接作为 RSS 的订阅链接。

后面我去了解了下 RSSHub,原来这是一个开源的 RSS 订阅源生成器,编写好规则(项目内称为脚本)后就能将内容按照 RSS 的语法生成一份订阅源,之后都可以用生成出来的链接在第三方阅读器上订阅和浏览。同时可以部署在自己的服务器里面,也能直接用 RSSHub 项目内别人预先编写好的规则,两者的效果是一样的。

粗浅了解下 RSSHub 的能力,以及也看了用该服务生成出来的订阅链接,功能上是能解决我的问题,但是时效性、准确度这些得自己部署完才能知道。

二楼回复认为静态的 XML 文件作为订阅源,会存在着不受我控制的缓存机制。于是乎我更坚定地认为应该尝试使用 RSSHub 的服务,毕竟这是动态生成,不是一个憨憨的静态文件。

安装 RSSHub
登录到服务器,由于我用的网站在阿里云轻服务器上搭建的,所以直接在网页里就能远程登录,其他服务器也能 ssh 登录后依次输入指令安装并运行 RSSHub。

git clone https://github.com/DIYgod/RSSHub.git
cd RSSHub
npm install
npm start

成功安装并运行后,在浏览器访问 1200 端口应该能看到如下页面。

回到终端,设置 RSSHub 服务常驻(断开 ssh 链接后仍然保持服务);以及开机自启。

# 设置常驻
npm install pm2 -g
pm2 start lib/index.js
# 设置开机自启
pm2 save
pm2 startup

这时可以访问几个 RSSHub 项目自带的路由,探索下其他有趣的 RSS 源。然后开始编写自己的脚本路由。

编写脚本路由
这一部分我的思路是参考文档的指引,并且在 RSSHub 项目内找到已有的脚本路由,找一份与自己网页结构类似的脚本,照葫芦画瓢捣鼓一份出来。

按文档的说法,/lib/router.js 里面记载了所有路由,即指明通过域名访问而指向的脚本。我先在里面创建一个路由,让其链接到之后创建的脚本。

# 添加路由
router.get('/feed', lazyloadRouteHandler('./routes/lrdim/blogs'));

其中 /feed 意思是我希望通过域名/feed 能够访问到我的 RSS 源,即 lrd.im/feed。后面的 ./routes/lrdim/blogs 意思是用前面的链接访问时,是链接到 /routes/lrdim/blogs.js 这个文件,用里面的规则生成 RSS 源。

然后到 /lib/routes 里面创建 lrdim 这个文件夹,并在里面创建 blog.js 这个文件,我们的抓取规则要写在里面。

由于我的网站是静态的 HTML 页面,所以我按照文档里的第二种方法,按照元素的标签和样式来抓取里面的数据。并且当时有参考了一个网页结构同样很简单的 RSSHub 内置源:十年之约。具体做法不在这详细说明了,主要按文档规范走就没啥问题,cheerio 语法也很直观容易理解。

保存后过几分钟,已经能在 lrd.im:1200/feed 中访问生成的 RSS 源了。此时需检查下名称、描述、标题、摘要、日期等有没有问题。

注意:日期必须按照格式编写,不能出现非法内容。否则这条 item 不会出现在结果中。我是写成了 YYYY-MM-DD。

调整渲染模版
由于刚刚通过设置 RSSHub 脚本获取到的数据,会经过 rss.art 模版处理后再成为 RSS 订阅源,所以我们需要打开 /lib/views/rss.art 对渲染模版作出相应的调整,比如预设 <webMaster> 不符合预期,调整为我自己的邮箱。

设置代理
至此,我的 RSS 订阅源已经生成完毕,内容、格式等符合要求,剩下的只有域名还不大对劲。

截至到上面的步骤,我的 RSS 订阅源扔人家需要通过端口来访问:lrd.im:1200/feed。这个肯定是不能作为最终产物的,谁会把端口直接暴露出来,太丢人了。于是乎需要配置最后一步:设置代理。

我的服务器里安装了 nginx,打开 nginx/conf/nginx.conf 后配置代理,将 lrd.im/feed 指向 1200 端口的页面。

location /feed {
proxy_pass http://127.0.0.1:1200;
}

此时测试 lrd.im/feed,已经能正常访问经 RSSHub 生成的订阅源。

验收
拿订阅链接去各主流阅读器中试验,尝试搜索、添加此链接,并尝试增删改被抓取的网页内容,检查到确实能够及时更新,且不会信息错乱。

大功告成!奉上我的设计博客正式的 RSS 订阅链接:https://lrd.im/feed

将该链接复制到 RSS 阅读器上就能够及时获取我的最新设计博客了。阅读器的话看个人喜好,目前我觉得 InoreaderReeder 的体验都不错。

做多啲:在网站中曝光 RSS 订阅功能

费了这么大周章捣鼓的新功能,起码在我的网站里多多曝光才是。所以我顺手做了三件事。

支持 RSSHub Radar

RSSHub 有一个浏览器插件叫 RSSHub Radar,用于探测网站上提供的 RSS,让读者能在浏览器的插件位置直接就能订阅,而不必在网页上费劲去找 RSS 订阅按钮。

支持该功能也不怎么复杂,我在所有页面的 <head> 处都添加了一条订阅地址,就能够被 RSSHub Radar 识别到。

博客页新功能提示

既然是我的博客内容的 RSS 源,那理所当然地应该在 lrd.im/blog.html 里添加一个稍微醒目一点的 Banner,提示到当前已经支持 RSS 啦!

此处参考了 Instagram 和 Apple 的样式,高亮若干秒之后开始减弱对比,使其融入到页面中。用了 CSS 的 keyframe 来做。

博客详情页功能组

从 Google Analytics 处跟踪到的数据,挺多流量是来自直接的博客详情页,而不是先从列表页跳转到详情页。所以也为了增加在详情页的曝光,我在「作者信息」一行右侧新添加了几个按钮,分别是:复制文章链接、RSS 订阅、联系小东。

原本「复制文章链接」这个功能我是做成响应式设计,只在移动端尺寸下才能看到,但还是根据 GA 数据反馈,造访我的博客的流量中有至少 75% 是来自桌面端,所以我将这个功能开放了。

「RSS 订阅」功能点击后能直接跳转到 lrd.im/feed,后续的订阅操作懂得都懂。每篇文章都提供这个入口,极大提高曝光量。

「联系小东」的功能是点击后出现我的微信二维码。我的设计博客通常会输出我的观点和看法,而我的网站里面又没有评论的功能,所以我捣鼓了一个微信二维码上去,读者如果有东西想和我交流就能直接加我好友一起交流。

这些功能没做成悬停在左右两侧,原因是我不太想在读者阅读时有其他干扰项。

哦对了,还顺便 Close 了一个,也是目前唯一一个 Issue。

总结

给博客生成 RSS 订阅源这事虽然一直惦记了有大半年,但实际开始到最终呈现,历时一周左右,都是用下班时间和周末探索的。

又到了给自己挖坑的时间:后面关于博客订阅这块,我会做的几件事:

  • 想办法将自己的 RSS 源链接提交到 RSSHub 项目下的博客分类
  • 研究主流的 RSS 阅读器抓取文章首图的规则。现在抓取的首图并非每次都符合我的预期,可能本身我的博客详情页构造需要作些许调整;
  • 探索开拓邮箱订阅功能的必要性。目前 Medium 能够用邮箱订阅我的最新博客动态,在想是否需要用到 Revue竹白之类的自己捣鼓一个邮件订阅服务,内嵌到个人网站里面去。顾虑是担心这种平台的文本编辑器功能是否够完善,以及和 Medium 功能重复了。

完成了自己的一项执念,如释重负,舒坦开朗。但心里仍然藏了另一项执念,我惦记了有至少一两年了,仍待推进…

参考文档

Hosting Ghost Blog with Docker on NixOS

By: Orchestr
20 August 2023 at 22:44
Hosting Ghost Blog with Docker on NixOS

As previously mentioned, I have successfully deployed NixOS on my Oracle ARM machine. You can find the original post here:

How to Install NixOS on Oracle ARM machine
The steps I undertook to install NixOS on an Oracle ARM machine.
Hosting Ghost Blog with Docker on NixOSDigital ImmigrantsOrchestr
Hosting Ghost Blog with Docker on NixOS

In the past, my blog was hosted on Tencent Cloud using Typecho. Unfortunately, due to unforeseen circumstances, I lost ownership of that machine along with all my previous posts. Consequently, I took a hiatus from blogging, remaining in a state of silence for a few years. However, I now realize the importance of reviving my blog before lethargy engulfs me.

After conducting extensive research and considering various platforms such as Ghost, WordPress, Typecho ,Hugo and some other platforms, I finally settled on Ghost. Its remarkable speed, plethora of customized themes, aesthetically pleasing web user interface, and integrated membership system influenced my decision.

Check out all the cool stuff Ghost has to offer on their website below:

Ghost: The Creator Economy Platform
The world’s most popular modern publishing platform for creating a new media platform. Used by Apple, SkyNews, Buffer, Kickstarter, and thousands more.
Hosting Ghost Blog with Docker on NixOSGhost - The Professional Publishing Platform
Hosting Ghost Blog with Docker on NixOS

Due to the absence of Ghost in the NixOS packages, and the cumbersome nature of adapting it into a NixOS service, Docker has emerged as an excellent solution for hosting Ghost. Here, I have provided a comprehensive breakdown of the steps I followed to set up a blog using Ghost with Docker on NixOS. This can be modified to use on other platforms.

Step 0: Enable Docker on NixOS

Enabling Docker(Podman) on NixOS is a straightforward process, requiring modification of just one configuration file. I personally prefer using the vim editor, but feel free to use your preferred tool such as nano, emacs, or VS Code.

The initial step involves logging into the machine, particularly if it is being used as a server.

ssh ${username}@${server IP}

Then, we can start to modify the configuration file:

sudo vim /etc/nixos/configuration.ni

There are two ways of adding Docker to the NixOS system: for all users:

environment.systemPackages = with pkgs; [
  docker
];

And for one user only:

users.users.${username}.packages = with pkgs; [
  docker
];

You can choose either way based on your needs. The next step is to enable the Docker service.

virtualisation.docker.enable = true;
virtualisation.oci-containers.backend = "docker";

Note that we're using oci-containers to control Dockers. If you have chosen to install Podman, remember to modify it accordingly. Some may question why we're not using docker-compose; this is a simple answer – we embrace the capabilities of NixOS, and that suffices.

Last, remember to create a directory for docker to use. Here's my example:

mkdir ~/.docker

Step 1: Set up Docker Network

Using the Docker CLI command docker network will indeed create the network, but it may not be the optimal approach. Since we're operating within the context of NixOS, we can add it as a service. Add the following code snippet to your configuration.nix file, ensuring to customize the name according to your requirements. In my case, I'm utilizing npm as an example since I'm employing nginx-proxy-manager as my Nginx reverse proxy service.

systemd.services.init-docker-ghost-network-and-files = {
  description = "Create the network npm for nginx proxy manager using reverse proxy.";
  after = [ "network.target" ];
  wantedBy = [ "multi-user.target" ];

  serviceConfig.Type = "oneshot";
  script =
    let dockercli = "${config.virtualisation.docker.package}/bin/docker";
    in ''
      # Put a true at the end to prevent getting non-zero return code, which will
      # crash the whole service.
      check=$(${dockercli} network ls | grep "npm" || true)
      if [ -z "$check" ]; then
        ${dockercli} network create npm
      else
        echo "npm already exists in docker"
      fi
    '';
};

Step 2: Set up Mysql for Ghost

We will now proceed with crafting Docker configurations. The initial step involves creating an external directory for MySQL to store its data, ensuring that we can modify MySQL without accessing the Docker environment directly. At present, this MySQL database is exclusively intended for Ghost; however, you have the freedom to tailor it according to your specific requirements.

mkdir ~/.docker/ghost-blog/mysql -p

Please add the following snippet to your configuration file as well:

virtualisation.oci-containers.containers."ghost-db" = {
  image = "mysql:latest";
  volumes = [ "/home/hua/.docker/ghost-blog/msql:/var/lib/mysql" ];
  environment = {
    MYSQL_ROOT_PASSWORD = "your_mysql_root_password";
    MYSQL_USER = "ghost";
    MYSQL_PASSWORD = "ghostdbpass";
    MYSQL_DATABASE = "ghostdb";
  };
  extraOptions = [ "--network=npm" ];
};

Please note that Ghost no longer supports SQLite and MariaDB as its database options.

Step 3: Set up Ghost Docker

Finally, It's time for Ghost.

Basic Set up Configuarion

Following the previous instructions, we will proceed to create the content folder:

mkdir ~/.docker/ghost-blog/content

Now, let's move on to configuring Ghost:

virtualisation.oci-containers.containers."ghost-blog" = {
  image = "ghost:latest";
  volumes =
    [ "/home/hua/.docker/ghost-blog/content:/var/lib/ghost/content" ];
  dependsOn = [ "ghost-db" ];
  ports = [ 3001:3001 ];
  environment = {
    NODE_ENV = "develop";
    url = "http://${server IP}:3001";
    database__client = "mysql";
    database__connection__host = "ghost-db";
    database__connection__user = "ghost";
    database__connection__password = "ghostdbpass";
    database__connection__database = "ghostdb";
  };
  extraOptions = [ "--network=npm" ];
};

Within this section, we configure the port mapping, environment variables, and volume mapping. Please note that you should customize the MySQL configurations in accordance with your specific setup in the final step.

Mail Server Set Up

Taking Gmail as an example, please note that you can modify this configuration according to your specific needs.

virtualisation.oci-containers.containers."ghost-blog".environment = {
  mail__transport = "SMTP";
  mail__option_service = "Google";
  mail__options__auth__user = "username@gmail.com";
  mail__options__auth__pass = "your google app password";
  mail__options__host = "smtp.gmail.com";
  mail__options__port = "587";
  mail__options__secure = "false";
  mail__from = "username@gmail.com";
  tls__rejectUnauthorized = "true";
}

Please remember that the Google app password mentioned here is different from your actual Google account password. You can generate a Google app password by following the steps outlined in the Sign in with app passwords guide.

By configuring these settings, visitors will be able to sign up and leave comments on our website.

More Custom Options

Please refer to the instructions provided on the Ghost website at the following link:

Configuration - Adapt your publication to suit your needs
Find out how to configure your Ghost publication or override Ghost’s default behaviour with robust config options, including mail, storage, scheduling and more!
Hosting Ghost Blog with Docker on NixOSGhost - The Professional Publishing Platform
Hosting Ghost Blog with Docker on NixOS

Step 4: Set up Nginx Reverse Proxy

There are numerous articles available on the internet that explain how to set up Nginx as a system service or utilize nginx-proxy-manager as a Docker service. For the purpose of this example, I will demonstrate the Docker service approach. Remember to create the necessary folders as well.

virtualisation.oci-containers.containers."nginx-proxy-manager" = {
  image = "jc21/nginx-proxy-manager:latest";
  dependsOn = [ "ghost-blog" "chatgpt-next-web" ];
  volumes = [
    "/home/hua/.docker/nginx-proxy-manager/data:/data",
    "/home/hua/.docker/nginx-proxy-manager/letsencrypt:/etc/letsencrypt"
  ];
  ports = [ "80:80", "443:443", "81:81" ];
  extraOptions = [ "--network=npm" ];
};

Step 5: Rebuild System

sudo nixos-rebuild switch`

Step 6: Start to Use

After rebuilding the system, you can proceed to open the web pages for both Ghost and nginx-proxy-manager.

For information and usage details about Ghost, please visit:

Ghost: The Creator Economy Platform
The world’s most popular modern publishing platform for creating a new media platform. Used by Apple, SkyNews, Buffer, Kickstarter, and thousands more.
Hosting Ghost Blog with Docker on NixOSGhost - The Professional Publishing Platform
Hosting Ghost Blog with Docker on NixOS

To learn more about nginx-proxy-manager, please visit:

Nginx Proxy Manager
Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let’s Encrypt
Hosting Ghost Blog with Docker on NixOS
Hosting Ghost Blog with Docker on NixOS

Please note that once you have set up the nginx reverse proxy for Ghost, it's necessary to modify the Docker configuration for Ghost as follows:

virtualisation.oci-containers.containers."ghost-blog".environment = {
  NODE_ENV = "production";
  url = "https://your-website-address";
}

Please replace your-website-address with the actual address of your website. After making this modification, rebuild the system again.

In conclusion, if you have any further questions, please feel free to leave a comment without hesitation.

苟日新

By: fivestone
5 September 2023 at 07:41

突然被人跑来问,是怎么做到写博客坚持这么久的,而且可以持续输出?

(荣幸地,拿起话筒:)啊,我不觉得我这个样子,叫做「持续输出」啦。早就连每月一更都不能保证了,而且那些技术相关的帖子,在我心里都不能算是「更新博客」的,用这些凑数也为我自己所不齿……

但我看到这个问题时,首先想到的,一个很重要的因素:大概是因为,这个站就一直在这儿吧~ 我的技术能力,不需要花什么额外的精力,就能让这个 blog 一直存活下去。于是,想写东西的时候,这里始终有个地方,可以让我写。

——也有很多时期,是完全写不下去的,长时期没法去面对、去反刍自己的生活;然而也没必要因此而关站,就让 blog 存活在那里,终归是个表述的出口。大概是因为,我也是希望,自己能够从那些「无法整理自己」的状态中,渐渐走出来,回复到可以写东西的状态吧。所以站点的持续存在,满重要的,因为确实能感觉到,想写点什么的时候,如果没有这么个站,又或者需要自己重新架一个,可能也就不写了……


这种「随时可以在站点写东西」的状态,也影响着对 blog 平台的选择(怎么又拐到技术贴去了?好吧,之前也一直想吐槽这方面,就顺带提一下)。这些年一直有 〖wordpress vs 各种静态博客〗哪个更好的争论。双方确实各有利弊。总体来说,静态博客最大的优点就是……省钱,可以薅 github、vercel 之类托管网站的羊毛。但另一方面,静态博客每次发布、或者修改一篇文章的过程,其实满折腾的。通常情况下,它需要

  • 一台固定的电脑,安装静态博客编译程序,并且从这台电脑发布到 github 的专门权限。而不是随便打开一台电脑或手机,从浏览器就能编辑发文;
  • 每次发文时的一系列专门操作。

我不乏看到有人,好久没有更新,突然想写一篇文章时,忘了怎么操作,翻出攻略来重温一遍;甚至忘了连接 github 的 ssh-key……可能别人觉得这样的折腾无所谓,或者自我管理优秀的话,不会出现这种情况。但我个人觉得,这是会在主观上,影响发文章的状态的。所以,随便在任何地方任何电脑上都能直观地发文,感觉还是蛮重要的。

好像也是可以通过一系列操作,实现用浏览器某个网站上编辑文章,然后自动编译发布到托管网站的。我没有仔细去关注。但是,如果把 blog 的生命周期,放到 5~10 年这个尺度上,那么这些网站之间的复杂依赖关系,很大程度上是不靠谱的。譬如我已经看到好几个静态 blog 的外挂评论系统,不知为什么不工作了……总之,相比之下,我可能更宁愿去使用那些免费带广告的 blog 平台。

我对写 blog 的新人的推荐,一直是——

  • 如果有技术能力、也有服务器的话,自建 wordpress;
  • 或者找人蹭一个。如果我们比较熟,你可以去买个域名,把 blog 挂在我的服务器上。这并不是很大的负担。(ps,个人 wordpress 小站,是可以不必安装开销很大的 mysql 数据库的);
  • 如果上面两条都不行,那么,我优先推荐去注册现成的 wordpress.com 或者 blogspot.com,目前看起来,长期靠谱的只有这两家了。虽然免费版界面不好看、还有广告,但长期写着应该没问题的;
  • 当然,我不会给乐于尝试静态博客的人泼冷水。但我会根据你的技术能力和气质,暗戳戳地担心:
    • 你能坚持写多久;
    • 你写出来的,会不会很多都是关于你怎么建站的经历和心得……

转一张,对于熟悉这十几年来 blog 平台变迁的人,应该会很搞笑:用不同工具写 blog 的人,(写 blog 文章)vs(写关于怎么配置 blog 的文章)的对比。右下角那些术语,都是在各个年代,需要各种不同程度的折腾的,静态 blog 方案:gatsby、org mode、jekyll、hugo、git workflow……


ps,两个月前,用这段代码方案,把我在 twitter 的所有 po 文,都导入到了自建的 mastodon 里。Twitter 那边,应该会随着 Elon Musk 的各种不靠谱折腾,渐渐放弃掉了吧。而每条推文的字数限制,从 twitter 的 140 字,变成 mastodon 的 500 字后,很多几百字的感受,要不要专门写到 blog 这边来,就比从前,更让人犹豫。具体怎么处理,我还没想好。

❌
❌