Single sanitization callback for multiple fields

How can I have a single sanitization callback for multiple fields and
then access the values of it ($input)?

For fields with the same validation logic, just set sanitize_callback to the same callback, e.g. if field_1 and field_2 are single-line and text-only fields (i.e. no HTML allowed), you can use 'sanitize_callback' => 'sanitize_text_field' for both fields. (see sanitize_text_field())

But for custom callbacks which need to access the option name (maybe because the callback is shared among different field types), then:

Option 1: Manually hook on sanitize_option_<option name>

register_setting() uses the sanitize_option_<option name> filter to register the sanitize callback, but the function calls add_filter() with the fourth parameter set to the default (i.e. 1) and thus, only one parameter that will be passed to the sanitize callback, so it won’t receive the option name (the $option parameter in the example below).

Therefore if you want to receive the option name, then do not set the sanitize_callback argument, and instead, manually hook on the above filter. E.g.

// Shared sanitize callback:
// Just a basic example of validating based on the option/input name.
function sanitize_any( $value, $option ) {
    switch ( $option ) {
        case 'field_1':
            if ( empty( $value ) ) {
                add_settings_error( 'my_plugin_messages', 'missing_field_1',
                    'Please enter a valid value into Field 1.' );

                // Use the current database value. This will cancel saving the option.
                $value = get_option( $option );
            }

            break;

        // .. your code ...
    }

    return $value;
}

// Register the settings:
register_setting( 'option_group_1', 'field_1', array( 'default' => '' ) );
register_setting( 'option_group_2', 'field_2', array( 'default' => '' ) );

// Hook on sanitize_option_field_1 and sanitize_option_field_2:
add_filter( 'sanitize_option_field_1', 'sanitize_any', 10, 2 );
add_filter( 'sanitize_option_field_2', 'sanitize_any', 10, 2 );

/* Sample <input>:
<input name="field_1" value="<?php echo esc_attr( get_option( 'field_1' ) ); ?>">
*/

Option 2: Use an array

If the inputs belong in the same option group (i.e. same form), you can store them in an array which means field_1 and field_2 would be in a single database option, e.g. one named my_fields. So for example:

// This function returns the default values for my_fields.
function my_fields_default_options() {
    return array(
        'field_1' => '',
        'field_2' => '',
    );
}

// The sanitize callback:
function sanitize_my_fields( $value ) {
    $value = (array) $value;

    if ( empty( $value['field_1'] ) ) {
        add_settings_error( 'my_plugin_messages', 'missing_field_1',
            'Please enter a valid value into Field 1.' );

        // Use the current database value. This will cancel saving the option.
        $value = get_option( 'my_fields' );
    }

    // .. your code ...

    return $value;
}

// Register the setting:
register_setting( 'my_option_group', 'my_fields', array(
    'sanitize_callback' => 'sanitize_my_fields',
    'default'           => my_fields_default_options(),
) );

/* And then in the <input> name, use the array format. E.g.
<input name="my_fields[field_1]" value="<?php echo esc_attr(
    get_option( 'my_fields', my_fields_default_options() )['field_1']
); ?>">
*/