Go语言类库-errors

Go语言错误处理
本文介绍Go语言中的错误处理机制,包括使用errors包自定义错误、处理预设错误、剥离错误上下文以及优雅地处理错误的方法。

初步认识

errors是Go语言官方提供的错误包,里面代码异常简洁,这里我贴一下:

// because the former will succeed if err wraps an *fs.PathError.
package errors

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}

type error interface {
	Error() string
}

errors包里面包含一个对外的方法New,主要用于自定义错误信息。例如:

func test(a, b int) {
	if a > b {
		errors.New("a比b大,抛出异常")
	}
}

这里New方法实际上是返回的是一个实现了error接口的类型的变量,这里是errorString结构体变量。error接口是Go语言官方提供的接口,里面含有一个方法Error() string,而errorString结构体实现了这个接口。

快速入门

package main

import (
	"errors"
	"fmt"
)

func main() {
	err := test(3, 1)
	if err != nil {
		fmt.Println(err)
	}

}

func test(a, b int) error {
	if a > b {
		return errors.New("a比b大,抛出异常")
	}
	return nil
}

test函数遇到逻辑错误,构建error类型的结构体变量返回;main函数判断err不等于nil,打印错误。

使用规范

视错误为值

Sentinel errors

哨兵错误。程序中遇到一个事先约定好的错误,处理流程不能再进行下去了,必须停下来,例如:

func readFile(path string) error {
	err := openFile(path)
	if err == io.EOF {
		log.Fatal("read failed:", err)
	}
}

第三行err == io.EOF是说error等于事先约定好的 io.EOF这个错误就程序停止往下执行。这里像一个问题:如果开发人员修改了这个错误或者加一些上下文信息,那上层判断“==”不就失效了吗,这里改如何处理呢?

package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	_, err := readFile("errortest/aa.txt")
	fmt.Println(err)
}

func readFile(path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("open failed: %w", err)
	}
	defer f.Close()
	buf, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, fmt.Errorf("read failed: %w", err)
	}
	return buf, nil
}

func Unwrap(err error) error {
	u, ok := err.(interface{ Unwrap() error })
	if !ok {
		return nil
	}
	return u.Unwrap()
}

上面这个代码readFile读文件,使用了fmt.Errorf(“open failed: %w”, err),在系统错误err本来是“open aa.txt: no such file or directory”,使用“%w”上加了“open failed:”,这样自定义的上下文描述就嵌套在系统错误上面了,返回给上层就是“open failed: open aa.txt: no such file or directory”,如果上层使用类似“err == io.EOF”就有问题了,这里需要先使用Unwarp来把错误自定义的上下文错误剥离出来,再使用类似“err == io.EOF”的语句做逻辑判断。
当然pkg/errors也提供了类似的解决方案。通过Warp可以把一个错误加上一个字符串包装成一个新的错误;通过Cause则可以进行相反的操作,将里层的错误还原。fmt.Printf(“%+v”, err)可以打印出错误堆栈。

package main

import (
	"fmt"
	"go-gin-api/pkg/errors"
	"io/ioutil"
	"os"
)

func main() {
	_, err := readFile("aa.txt")
	//fmt.Println(err)
	if err != nil {
		fmt.Printf("%+v", err)
		os.Exit(1)
	}
}

func readFile(path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		//return nil, fmt.Errorf("open failed: %w", err)
		return nil, errors.Wrap(err, "open failed")
	}
	defer f.Close()
	buf, err := ioutil.ReadAll(f)
	if err != nil {
		//return nil, fmt.Errorf("read failed: %w", err)
		return nil, errors.Wrap(err, "read failed: %w")
	}
	return buf, nil
}
Error Types

error的类型。指的是实现了error接口的哪些类型。他的一个好处是类型中除了error外,还可以附带其它字段,从而提供额外信息。

type PathError struct {
	Op string
    Path string
    Err error
}

题外话:PathError结构体有error字段,说明这个结构体的Err属性是实现了error接口的类型的对象,于是就会有error接口的方法,所有PathError的对象也会有error接口的方法,也就是说PathError实现了error接口。

func getError(err error)error {
    switch err := err.(type) {
        case *PathError:
        	return err.Err
        case *OtherError:
        	return err.Err
    }
}

这样做的不好的地方是自定义的错误和使用错误的包之间形成了依赖关系,代码不够优雅。

Opaque errors

黑盒errors。能知道错误发生了,但是无法看到他内部到底是什么,不知道具体类型是什么。

func fn() error {
    x,err := =bar.Foo()
    if err !=nil {
        return err
    }
    return nil
}

一旦出错,直接返回,终止后续操作。但是项目开发中比如请求外部接口超时,需要重新请求,这个时候就无法做到。

检查并优雅的处理错误

如上Sentinel errors的处理方式便是检查并优雅鹅处理错误的案例。

只处理错误一次

	if err != nil {
		//return nil, fmt.Errorf("open failed: %w", err)
		log.Fatal(err)
		return nil, errors.Wrap(err, "open failed")
	}

这里既吧错误记录到日志了还向上返回了错误。这样做的后果是日志上有好多统一的错误信息。合理做法只处理错误一次,要么记录日志要么向上返回。通常项目开发中底层方法向上返回错误,上层方法记录日志。

<think>我们有一组命令: rm .xsession-errors.old; touch .xsession-errors.old; sudo chattr +i .xsession-errors.old; rm .xsession-errors; touch .xsession-errors; sudo chattr +i .xsession-errors 用途: 根据引用[1]和[2],这两个文件(.xsession-errors 和 .xsession-errors.old)是Linux桌面环境下记录用户会话错误日志的文件。随着时间推移,它们可能会变得非常大,占用磁盘空间。对于不需要这些日志的普通用户,可以通过锁定文件(设置不可修改属性)来防止它们增长。 这组命令的作用是: 1. 删除现有的.xsession-errors.old文件(如果存在) 2. 创建一个新的空文件.xsession-errors.old 3. 使用chattr命令设置该文件的不可修改属性(i属性),这样文件就不能被修改(包括写入、删除等) 4. 同样,对.xsession-errors文件执行相同的操作:删除、创建空文件、设置不可修改属性。 使用场景: 当用户发现这两个日志文件占用了过多磁盘空间,并且用户不需要这些错误日志时,可以使用这组命令来防止它们继续增长。 可能出现的问题及解决方法: 1. 问题:执行删除命令(rm)时,如果文件不存在,会报错“No such file or directory”。 解决方法:可以在删除前检查文件是否存在,或者忽略错误(使用`rm -f`)。 2. 问题:使用`chattr`命令需要管理员权限,所以需要sudo。如果当前用户没有sudo权限,则无法执行。 解决方法:确保当前用户在sudoers列表中,或者由管理员执行。 3. 问题:设置不可修改属性后,如果需要再次查看日志(例如排查问题),则无法写入,也无法删除(即使使用`rm -f`也不行)。 解决方法:在需要重新启用日志时,使用`sudo chattr -i`命令移除不可修改属性,然后就可以删除或清空文件。例如: sudo chattr -i ~/.xsession-errors sudo chattr -i ~/.xsession-errors.old 然后就可以按需处理文件(删除或清空)。 4. 问题:如果系统或应用程序需要写入这些日志文件,设置不可修改属性后,可能会导致某些程序无法记录错误,甚至可能影响程序运行(尽管引用[2]提到对于日常使用者来说没有必要,但某些程序可能会尝试写入并失败)。 解决方法:如果遇到应用程序因无法写入日志而报错,可以考虑移除不可修改属性(如上所述),或者定期清理这些文件(例如通过cron任务定期清空文件)而不是锁定。 5. 问题:在创建新文件(touch)和设置不可修改属性之间,可能会有程序写入日志,导致文件在锁定前已经有一定内容。 解决方法:这个时间窗口通常很小,但如果需要确保文件完全为空且不被写入,可以在执行命令时暂时退出所有图形应用程序(或切换到tty终端执行),然后再执行命令。 改进的命令(避免文件不存在的错误): 我们可以使用`rm -f`来忽略不存在的文件,这样即使文件不存在也不会报错。 因此,改进后的命令如下: ``` rm -f ~/.xsession-errors.old touch ~/.xsession-errors.old sudo chattr +i ~/.xsession-errors.old rm -f ~/.xsession-errors touch ~/.xsession-errors sudo chattr +i ~/.xsession-errors ``` 注意:命令中的路径使用了`~`,代表当前用户的家目录。 另外,引用[1]中提到了另一种清空文件的方法(使用`echo '' >`),但这里使用`rm`后再`touch`创建空文件效果相同。 引用[3]是关于设置用户密码有效期的,与本问题无关,故忽略。 引用说明: 根据引用[1]和[2],我们了解到这两个文件的作用以及锁定它们的方法[^1][^2]。 总结: 这组命令用于防止Linux桌面环境下的错误日志文件(.xsession-errors和.xsession-errors.old)不断增长,通过设置不可修改属性来锁定文件。适用于希望节省磁盘空间且不需要这些日志的用户。但需要注意,锁定后可能影响错误日志的记录,在需要调试时需临时解除锁定。 相关问题: 根据用户要求,生成相关问题。 </think> ### 命令用途与使用场景 该命令序列用于**永久锁定 Linux 桌面环境的错误日志文件**(`.xsession-errors` 和 `.xsession-errors.old`),防止它们无限增长占用磁盘空间。核心操作分三步: 1. **删除旧文件**:`rm .xsession-errors*` 2. **创建空文件**:`touch .xsession-errors*` 3. **设置不可修改属性**:`sudo chattr +i` **使用场景**: - 当 `.xsession-errors` 或 `.xsession-errors.old` 文件体积过大(如达到 GB 级别),占用大量磁盘空间[^2]。 - 用户无需依赖这些日志排查错误,且希望一劳永逸阻止文件再生[^2]。 --- ### 可能出现的问题及解决方法 #### 1. **文件权限不足导致命令失败** - **问题**:执行 `sudo chattr +i` 时若用户无 `sudo` 权限,会报错 `Permission denied`。 - **解决**: ```bash su - # 切换至 root 用户后再执行命令 chattr +i /home/用户名/.xsession-errors* # 替换为实际用户名 ``` #### 2. **系统或应用依赖日志写入** - **问题**:部分桌面环境(如 GNOME/KDE)或应用可能因无法写入日志而崩溃[^2]。 - **解决**: - 临时解除锁定: ```bash sudo chattr -i ~/.xsession-errors* # 移除不可修改属性 ``` - 调试后重新锁定: ```bash > ~/.xsession-errors # 清空内容 sudo chattr +i ~/.xsession-errors* # 重新锁定 ``` #### 3. **文件删除后自动再生导致锁定失效** - **问题**:在 `rm` 和 `touch` 的间隙,系统可能重新生成文件并写入内容,导致锁定前已存在数据。 - **解决**: ```bash touch ~/.xsession-errors # 先创建空文件 sudo chattr +i ~/.xsession-errors # 立即锁定 # 对 .xsession-errors.old 重复相同操作 ``` #### 4. **需要恢复日志功能** - **问题**:后续若需调试错误,锁定文件会导致无法记录新日志。 - **解决**: - 移除不可修改属性并定期清理: ```bash sudo chattr -i ~/.xsession-errors* # 解锁 echo '' > ~/.xsession-errors # 手动清空(或设置 cron 定时任务) ``` --- ### 命令原理解析 - **`chattr +i`**:设置文件的 **不可修改(immutable)属性**,阻止任何写入、删除或重命名操作,即使 root 用户也无法直接修改。 - **先删后建**:确保操作前文件无残留内容,避免锁定非空文件。 - **副作用**:锁定后系统将无法记录桌面环境错误,可能影响故障诊断。 > **替代方案**:若需保留日志但限制大小,可使用 `logrotate` 配置轮转策略: > ```bash > sudo nano /etc/logrotate.conf # 添加以下配置 > /home/用户名/.xsession-errors { > rotate 3 # 保留3份旧日志 > size 100M # 超过100MB时轮转 > compress # 压缩旧日志 > } > ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值