Skip to Content
Course content

114: strings.Contains, Index, Replace

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I was digging through some legacy log files the other day and realized I needed to pull out specific error messages, but the logs were a mess. Some lines had timestamps, some had priority levels, and some were just raw dumps. I found myself needing to quickly scan for keywords and clean up the text before sending it to a reporting tool.

Just checking for a needle in a haystack

When I first looked at a log line like "2023-10-12 14:02:01 [ERROR] Database connection failed: timeout", my first instinct was just to see if the word "ERROR" was even there. I didn't care where it was; I just needed a true or false.

package main

import (
	"fmt"
	"strings"
)

func main() {
	logLine := "2023-10-12 14:02:01 [ERROR] Database connection failed: timeout"
	
	if strings.Contains(logLine, "ERROR") {
		fmt.Println("Found an error!")
	}
}

This is the simplest tool in the strings package. strings.Contains takes the source string and the substring you're looking for. It returns a boolean. It's efficient, it's readable, and for 90% of "does this exist?" cases, it's all you need.

Pinpointing the exact spot

But here's where I hit a snag. Knowing that "ERROR" exists isn't helpful if I want to strip away the timestamp and only keep the actual message. I need to know where the error starts.

I tried using strings.Index to see if it could give me a coordinate.

package main

import (
	"fmt"
	"strings"
)

func main() {
	logLine := "2023-10-12 14:02:01 [ERROR] Database connection failed: timeout"
	idx := strings.Index(logLine, "[ERROR]")
	
	fmt.Printf("Index: %d\n", idx)
	// Output: Index: 20
}

Okay, so it's at index 20. Now I can use a slice to grab everything from that point forward. But wait—what happens if the word isn't there? I'll try it with "INFO" instead.

logLine := "2023-10-12 14:02:01 [DEBUG] Heartbeat sent"
idx := strings.Index(logLine, "[ERROR]")
fmt.Printf("Index: %d\n", idx) 
// Output: Index: -1

Ah, right. Index returns -1 if the substring isn't found. If I had blindly tried to slice the string using logLine[idx:] without checking for -1, my program would have panicked with an "out of range" error. Always check for that -1 before you slice.

Swapping text on the fly

Lastly, I noticed these logs were being shared with a client, and they contained internal server names like "srv-prod-01" that I didn't want them to see. I needed a way to swap those out for something generic.

I'll try strings.Replace. I remember from the docs that this function requires a bit more information than just "find and replace."

package main

import (
	"fmt"
	"strings"
)

func main() {
	msg := "Connection lost on srv-prod-01 and srv-prod-02"
	
	// I want to replace "srv-prod" with "server"
	// strings.Replace(s, old, new, n)
	cleaned := strings.Replace(msg, "srv-prod", "server", 1)
	fmt.Println(cleaned)
}

The output is: "Connection lost on server-01 and srv-prod-02". Only the first one changed! That's because of that last argument n. In Go, strings.Replace asks you exactly how many occurrences you want to swap. If I only put 1, it stops after the first hit.

Since I want to scrub every instance of that string, I have two choices. I could put a huge number like 1000, but that's amateur hour. The idiomatic way to say "do it all" is to pass -1, or just use strings.ReplaceAll, which is essentially a wrapper that does exactly that.

cleanedAll := strings.ReplaceAll(msg, "srv-prod", "server")
fmt.Println(cleanedAll)
// Output: Connection lost on server-01 and server-02

And there we have it. Check for existence with Contains, find the location with Index, and clean up the mess with ReplaceAll.




📋 Practical Task

The Log Entry Anonymizer

You are building a tool to sanitize logs before they are uploaded to a public forum. You need to write a function that processes a log string and performs the following steps:

  • Check if the log line contains the substring "USER_ID:". If it doesn't, return the original string immediately.
  • If it does contain "USER_ID:", find the index where that substring starts.
  • Extract the substring starting from "USER_ID:" up until the first space that follows it (or the end of the string).
  • Replace that entire identified user ID segment (e.g., "USER_ID:12345") with the text "USER_ID:[REDACTED]".

Example input: "10:00:01 Login attempt by USER_ID:99887 from IP 1.1.1.1"
Example output: "10:00:01 Login attempt by USER_ID:[REDACTED] from IP 1.1.1.1"

Rating
0 0

There are no comments for now.

to be the first to leave a comment.